github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/modules/renter/hostdb/hostweight.go (about)

     1  package hostdb
     2  
     3  import (
     4  	"math"
     5  	"math/big"
     6  
     7  	"SiaPrime/build"
     8  	"SiaPrime/modules"
     9  	"SiaPrime/modules/renter/hostdb/hosttree"
    10  	"SiaPrime/types"
    11  )
    12  
    13  const (
    14  	// collateralExponentiation is the power to which we raise the weight
    15  	// during collateral adjustment when the collateral is large. This sublinear
    16  	// number ensures that there is not an overpreference on collateral when
    17  	// collateral is large relative to the size of the allowance.
    18  	collateralExponentiationLarge = 0.5
    19  
    20  	// collateralExponentiationSmall is the power to which we raise the weight
    21  	// during collateral adjustment when the collateral is small. This large
    22  	// number ensures a heavy focus on collateral when distinguishing between
    23  	// hosts that have a very small amount of collateral provided compared to
    24  	// the size of the allowance.
    25  	//
    26  	// The number is set relative to the price exponentiation, because the goal
    27  	// is to ensure that the collateral has more weight than the price when the
    28  	// collateral is small.
    29  	collateralExponentiationSmall = priceExponentiationLarge + 1
    30  
    31  	// collateralFloor is a part of the equation for determining the collateral
    32  	// cutoff between large and small collateral. The equation figures out how
    33  	// much collateral is expected given the allowance, and then divided by
    34  	// 'collateralFloor' so that the cutoff for how much collateral counts as
    35  	// 'not much' is reasonably below what we are actually expecting from the
    36  	// host.
    37  	collateralFloor = 2
    38  
    39  	// interactionExponentiation determines how heavily we penalize hosts for
    40  	// having poor interactions - disconnecting, RPCs with errors, etc. The
    41  	// exponentiation is very high because the renter will already intentionally
    42  	// avoid hosts that do not have many successful interactions, meaning that
    43  	// the bad points do not rack up very quickly.
    44  	interactionExponentiation = 10
    45  
    46  	// priceExponentiationLarge is the number of times that the weight is
    47  	// divided by the price when the price is large relative to the allowance.
    48  	// The exponentiation is a lot higher because we care greatly about high
    49  	// priced hosts.
    50  	priceExponentiationLarge = 5
    51  
    52  	// priceExponentiationSmall is the number of times that the weight is
    53  	// divided by the price when the price is small relative to the allowance.
    54  	// The exponentiation is lower because we do not care about saving
    55  	// substantial amounts of money when the price is low.
    56  	priceExponentiationSmall = 0.75
    57  
    58  	// priceFloor is used in the final step of the equation that determines the
    59  	// cutoff for where a low price no longer counts as interesting to the
    60  	// renter. A priceFloor of '5' means that if the host can provide us with
    61  	// the amount of storage we require for less than 20% of the total
    62  	// allowance, then we switch to a new equation where further decreases in
    63  	// price are valued much less aggressively (though they are still valued).
    64  	priceFloor = 5
    65  
    66  	// tbMonth is the number of bytes in a terabyte times the number of blocks
    67  	// in a month.
    68  	tbMonth = 4032 * 1e12
    69  )
    70  
    71  var (
    72  	// Because most weights would otherwise be fractional, we set the base
    73  	// weight to be very large.
    74  	baseWeight = types.NewCurrency(new(big.Int).Exp(big.NewInt(10), big.NewInt(80), nil))
    75  
    76  	// priceDiveNormalization reduces the raw value of the price so that not so
    77  	// many digits are needed when operating on the weight. This also allows the
    78  	// base weight to be a lot lower.
    79  	priceDivNormalization = types.SiacoinPrecision.Div64(1e3).Div64(tbMonth)
    80  
    81  	// requiredStorage indicates the amount of storage that the host must be
    82  	// offering in order to be considered a valuable/worthwhile host.
    83  	requiredStorage = build.Select(build.Var{
    84  		Standard: uint64(20e9),
    85  		Dev:      uint64(1e6),
    86  		Testing:  uint64(1e3),
    87  	}).(uint64)
    88  )
    89  
    90  // collateralAdjustments improves the host's weight according to the amount of
    91  // collateral that they have provided.
    92  func (hdb *HostDB) collateralAdjustments(entry modules.HostDBEntry, allowance modules.Allowance, ug modules.UsageGuidelines) float64 {
    93  	// Ensure that all values will avoid divide by zero errors.
    94  	if allowance.Hosts == 0 {
    95  		allowance.Hosts = 1
    96  	}
    97  	if allowance.Period == 0 {
    98  		allowance.Period = 1
    99  	}
   100  	if ug.ExpectedStorage == 0 {
   101  		ug.ExpectedStorage = 1
   102  	}
   103  	if ug.ExpectedUploadFrequency == 0 {
   104  		ug.ExpectedUploadFrequency = 1
   105  	}
   106  	if ug.ExpectedDownloadFrequency == 0 {
   107  		ug.ExpectedDownloadFrequency = 1
   108  	}
   109  	if ug.ExpectedRedundancy == 0 {
   110  		ug.ExpectedRedundancy = 1
   111  	}
   112  
   113  	// Ensure that the allowance and expected storage will not brush up against
   114  	// the max collateral. If the allowance comes within half of the max
   115  	// collateral, cap the collateral that we use during adjustments based on
   116  	// the max collateral instead of the per-byte collateral.
   117  	//
   118  	// The purpose of this code is to make sure that the host actually has a
   119  	// high enough MaxCollateral to cover all of the data that we intend to
   120  	// store with the host at the collateral price that the host is advertising.
   121  	// We add a 2x buffer to account for the fact that the renter may end up
   122  	// storing extra data on this host.
   123  	hostCollateral := entry.Collateral
   124  	possibleCollateral := entry.MaxCollateral.Div64(uint64(allowance.Period)).Div64(ug.ExpectedStorage).Div64(2)
   125  	if possibleCollateral.Cmp(hostCollateral) < 0 {
   126  		hostCollateral = possibleCollateral
   127  	}
   128  
   129  	// Determine the cutoff for the difference between small collateral and
   130  	// large collateral. The cutoff is used to create a step function in the
   131  	// collateral scoring where decreasing collateral results in much higher
   132  	// penalties below a certain threshold.
   133  	//
   134  	// This threshold is attempting to be the threshold where the amount of
   135  	// money becomes insignificant. A collateral that is 10x higher than the
   136  	// price is not interesting, compelling, nor a sign of reliability if the
   137  	// price and collateral are both effectively zero.
   138  	//
   139  	// The strategy is to take our total allowance and divide it by the number
   140  	// of hosts, to get an expected allowance per host. We then adjust based on
   141  	// the period, and then adjust by adding in the expected storage, upload and
   142  	// download. We add the three together so that storage heavy allowances will
   143  	// have higher expectations for collateral than bandwidth heavy allowances.
   144  	// Finally, we divide the whole thing by collateralFloor to give some wiggle room to
   145  	// hosts. The large multiplier provided for low collaterals is only intended
   146  	// to discredit hosts that have a meaningless amount of collateral.
   147  	expectedUploadBandwidth := ug.ExpectedStorage * uint64(allowance.Period) / ug.ExpectedUploadFrequency
   148  	expectedDownloadBandwidthRedundant := ug.ExpectedStorage * uint64(allowance.Period) / ug.ExpectedDownloadFrequency
   149  	expectedDownloadBandwidth := uint64(float64(expectedDownloadBandwidthRedundant) / ug.ExpectedRedundancy)
   150  	expectedBandwidth := expectedUploadBandwidth + expectedDownloadBandwidth
   151  	cutoff := allowance.Funds.Div64(allowance.Hosts).Div64(uint64(allowance.Period)).Div64(ug.ExpectedStorage + expectedBandwidth).Div64(collateralFloor)
   152  	if hostCollateral.Cmp(cutoff) < 0 {
   153  		// Set the cutoff equal to the collateral so that the ratio has a
   154  		// minimum of 1, and also so that the smallWeight is computed based on
   155  		// the actual collateral instead of just the cutoff.
   156  		cutoff = hostCollateral
   157  	}
   158  	// Get the ratio between the cutoff and the actual collateral so we can
   159  	// award the bonus for having a large collateral. Perform normalization
   160  	// before converting to uint64.
   161  	collateral64, _ := hostCollateral.Div(priceDivNormalization).Uint64()
   162  	cutoff64, _ := cutoff.Div(priceDivNormalization).Uint64()
   163  	if cutoff64 == 0 {
   164  		cutoff64 = 1
   165  	}
   166  	ratio := float64(collateral64) / float64(cutoff64)
   167  
   168  	// Use the cutoff to determine the score based on the small exponentiation
   169  	// factor (which has a high exponentiation), and then use the ratio between
   170  	// the two to determine the bonus gained from having a high collateral.
   171  	smallWeight := math.Pow(float64(cutoff64), collateralExponentiationSmall)
   172  	largeWeight := math.Pow(ratio, collateralExponentiationLarge)
   173  	return smallWeight * largeWeight
   174  }
   175  
   176  // interactionAdjustments determine the penalty to be applied to a host for the
   177  // historic and currnet interactions with that host. This function focuses on
   178  // historic interactions and ignores recent interactions.
   179  func (hdb *HostDB) interactionAdjustments(entry modules.HostDBEntry) float64 {
   180  	// Give the host a baseline of 30 successful interactions and 1 failed
   181  	// interaction. This gives the host a baseline if we've had few
   182  	// interactions with them. The 1 failed interaction will become
   183  	// irrelevant after sufficient interactions with the host.
   184  	hsi := entry.HistoricSuccessfulInteractions + 30
   185  	hfi := entry.HistoricFailedInteractions + 1
   186  
   187  	// Determine the intraction ratio based off of the historic interactions.
   188  	ratio := float64(hsi) / float64(hsi+hfi)
   189  	return math.Pow(ratio, interactionExponentiation)
   190  }
   191  
   192  // priceAdjustments will adjust the weight of the entry according to the prices
   193  // that it has set.
   194  func (hdb *HostDB) priceAdjustments(entry modules.HostDBEntry, allowance modules.Allowance, ug modules.UsageGuidelines) float64 {
   195  	// Divide by zero mitigation.
   196  	if allowance.Hosts == 0 {
   197  		allowance.Hosts = 1
   198  	}
   199  	if allowance.Period == 0 {
   200  		allowance.Period = 1
   201  	}
   202  	if ug.ExpectedStorage == 0 {
   203  		ug.ExpectedStorage = 1
   204  	}
   205  	if ug.ExpectedUploadFrequency == 0 {
   206  		ug.ExpectedUploadFrequency = 1
   207  	}
   208  	if ug.ExpectedDownloadFrequency == 0 {
   209  		ug.ExpectedDownloadFrequency = 1
   210  	}
   211  	if ug.ExpectedRedundancy == 0 {
   212  		ug.ExpectedRedundancy = 1
   213  	}
   214  
   215  	// Calculate the hostCollateral the renter would expect the host to put
   216  	// into a contract.
   217  	// TODO: Use actual transaction fee estimation instead of hardcoded 1SC.
   218  	_, _, hostCollateral, err := modules.RenterPayoutsPreTax(entry, allowance.Funds.Div64(allowance.Hosts), types.SiacoinPrecision, types.ZeroCurrency, allowance.Period, ug.ExpectedStorage)
   219  	if err != nil {
   220  		hdb.log.Println(err)
   221  		return 0
   222  	}
   223  
   224  	// Prices tiered as follows:
   225  	//    - the collateral price is presented as 'per block per byte'
   226  	//    - the storage price is presented as 'per block per byte'
   227  	//    - the contract price is presented as a flat rate
   228  	//    - the upload bandwidth price is per byte
   229  	//    - the download bandwidth price is per byte
   230  	//
   231  	// The adjusted prices take the pricing for other parts of the contract
   232  	// (like bandwidth and fees) and convert them into terms that are relative
   233  	// to the storage price.
   234  	adjustedCollateralPrice := hostCollateral.Div64(uint64(allowance.Period)).Div64(ug.ExpectedStorage)
   235  	adjustedContractPrice := entry.ContractPrice.Div64(uint64(allowance.Period)).Div64(ug.ExpectedStorage)
   236  	adjustedUploadPrice := entry.UploadBandwidthPrice.Div64(ug.ExpectedUploadFrequency)
   237  	adjustedDownloadPrice := entry.DownloadBandwidthPrice.Div64(ug.ExpectedDownloadFrequency).MulFloat(ug.ExpectedRedundancy)
   238  	siafundFee := adjustedContractPrice.Add(adjustedUploadPrice).Add(adjustedDownloadPrice).Add(adjustedCollateralPrice).MulTax()
   239  	totalPrice := entry.StoragePrice.Add(adjustedContractPrice).Add(adjustedUploadPrice).Add(adjustedDownloadPrice).Add(siafundFee)
   240  
   241  	// Determine a cutoff for whether the total price is considered a high price
   242  	// or a low price. This cutoff attempts to determine where the price becomes
   243  	// insignificant.
   244  	expectedUploadBandwidth := ug.ExpectedStorage * uint64(allowance.Period) / ug.ExpectedUploadFrequency
   245  	expectedDownloadBandwidthRedundant := ug.ExpectedStorage * uint64(allowance.Period) / ug.ExpectedDownloadFrequency
   246  	expectedDownloadBandwidth := uint64(float64(expectedDownloadBandwidthRedundant) / ug.ExpectedRedundancy)
   247  	expectedBandwidth := expectedUploadBandwidth + expectedDownloadBandwidth
   248  	cutoff := allowance.Funds.Div64(allowance.Hosts).Div64(uint64(allowance.Period)).Div64(ug.ExpectedStorage + expectedBandwidth).Div64(priceFloor)
   249  	if totalPrice.Cmp(cutoff) < 0 {
   250  		cutoff = totalPrice
   251  	}
   252  	price64, _ := totalPrice.Div(priceDivNormalization).Uint64()
   253  	cutoff64, _ := cutoff.Div(priceDivNormalization).Uint64()
   254  	if cutoff64 == 0 {
   255  		cutoff64 = 1
   256  	}
   257  	if price64 == 0 {
   258  		price64 = 1
   259  	}
   260  	ratio := float64(price64) / float64(cutoff64)
   261  
   262  	smallWeight := math.Pow(float64(cutoff64), priceExponentiationSmall)
   263  	largeWeight := math.Pow(ratio, priceExponentiationLarge)
   264  	return 1 / (smallWeight * largeWeight)
   265  }
   266  
   267  // storageRemainingAdjustments adjusts the weight of the entry according to how
   268  // much storage it has remaining.
   269  func storageRemainingAdjustments(entry modules.HostDBEntry) float64 {
   270  	base := float64(1)
   271  	if entry.RemainingStorage < 100*requiredStorage {
   272  		base = base / 2 // 2x total penalty
   273  	}
   274  	if entry.RemainingStorage < 80*requiredStorage {
   275  		base = base / 2 // 4x total penalty
   276  	}
   277  	if entry.RemainingStorage < 40*requiredStorage {
   278  		base = base / 2 // 8x total penalty
   279  	}
   280  	if entry.RemainingStorage < 20*requiredStorage {
   281  		base = base / 2 // 16x total penalty
   282  	}
   283  	if entry.RemainingStorage < 15*requiredStorage {
   284  		base = base / 2 // 32x total penalty
   285  	}
   286  	if entry.RemainingStorage < 10*requiredStorage {
   287  		base = base / 2 // 64x total penalty
   288  	}
   289  	if entry.RemainingStorage < 5*requiredStorage {
   290  		base = base / 2 // 128x total penalty
   291  	}
   292  	if entry.RemainingStorage < 3*requiredStorage {
   293  		base = base / 2 // 256x total penalty
   294  	}
   295  	if entry.RemainingStorage < 2*requiredStorage {
   296  		base = base / 2 // 512x total penalty
   297  	}
   298  	if entry.RemainingStorage < requiredStorage {
   299  		base = base / 2 // 1024x total penalty
   300  	}
   301  	return base
   302  }
   303  
   304  // versionAdjustments will adjust the weight of the entry according to the siad
   305  // version reported by the host.
   306  func versionAdjustments(entry modules.HostDBEntry) float64 {
   307  	base := float64(1)
   308  	if build.VersionCmp(entry.Version, "1.4.0") < 0 {
   309  		base = base * 0.99999 // Safety value to make sure we update the version penalties every time we update the host.
   310  	}
   311          // -10% for being below 1.3.8.
   312          if build.VersionCmp(entry.Version, "1.3.8") < 0 {
   313                  base = base * 0.9
   314          } 
   315  	// -10% for being below 1.3.5.
   316  	if build.VersionCmp(entry.Version, "1.3.5") < 0 {
   317  		base = base * 0.9
   318  	}
   319  	// -10% for being below 1.3.4.
   320  	if build.VersionCmp(entry.Version, "1.3.4") < 0 {
   321  		base = base * 0.9
   322  	}
   323  	// -10% for being below 1.3.3.
   324  	if build.VersionCmp(entry.Version, "1.3.3") < 0 {
   325  		base = base * 0.9
   326  	}
   327  	// we shouldn't use pre hardfork hosts
   328  	if build.VersionCmp(entry.Version, "1.3.1") < 0 {
   329  		base = math.SmallestNonzeroFloat64
   330  	}
   331  	return base
   332  }
   333  
   334  // lifetimeAdjustments will adjust the weight of the host according to the total
   335  // amount of time that has passed since the host's original announcement.
   336  func (hdb *HostDB) lifetimeAdjustments(entry modules.HostDBEntry) float64 {
   337  	base := float64(1)
   338  	if hdb.blockHeight >= entry.FirstSeen {
   339  		age := hdb.blockHeight - entry.FirstSeen
   340  		if age < 12000 {
   341  			base = base * 2 / 3 // 1.5x total
   342  		}
   343  		if age < 6000 {
   344  			base = base / 2 // 3x total
   345  		}
   346  		if age < 4000 {
   347  			base = base / 2 // 6x total
   348  		}
   349  		if age < 2000 {
   350  			base = base / 2 // 12x total
   351  		}
   352  		if age < 1000 {
   353  			base = base / 3 // 36x total
   354  		}
   355  		if age < 576 {
   356  			base = base / 3 // 108x total
   357  		}
   358  		if age < 288 {
   359  			base = base / 3 // 324x total
   360  		}
   361  		if age < 144 {
   362  			base = base / 3 // 972x total
   363  		}
   364  	}
   365  	return base
   366  }
   367  
   368  // uptimeAdjustments penalizes the host for having poor uptime, and for being
   369  // offline.
   370  //
   371  // CAUTION: The function 'updateEntry' will manually fill out two scans for a
   372  // new host to give the host some initial uptime or downtime. Modification of
   373  // this function needs to be made paying attention to the structure of that
   374  // function.
   375  func (hdb *HostDB) uptimeAdjustments(entry modules.HostDBEntry) float64 {
   376  	// Special case: if we have scanned the host twice or fewer, don't perform
   377  	// uptime math.
   378  	if len(entry.ScanHistory) == 0 {
   379  		return 0.25
   380  	}
   381  	if len(entry.ScanHistory) == 1 {
   382  		if entry.ScanHistory[0].Success {
   383  			return 0.75
   384  		}
   385  		return 0.25
   386  	}
   387  	if len(entry.ScanHistory) == 2 {
   388  		if entry.ScanHistory[0].Success && entry.ScanHistory[1].Success {
   389  			return 0.85
   390  		}
   391  		if entry.ScanHistory[0].Success || entry.ScanHistory[1].Success {
   392  			return 0.50
   393  		}
   394  		return 0.05
   395  	}
   396  
   397  	// Compute the total measured uptime and total measured downtime for this
   398  	// host.
   399  	downtime := entry.HistoricDowntime
   400  	uptime := entry.HistoricUptime
   401  	recentTime := entry.ScanHistory[0].Timestamp
   402  	recentSuccess := entry.ScanHistory[0].Success
   403  	for _, scan := range entry.ScanHistory[1:] {
   404  		if recentTime.After(scan.Timestamp) {
   405  			if build.DEBUG {
   406  				hdb.log.Critical("Host entry scan history not sorted.")
   407  			} else {
   408  				hdb.log.Print("WARNING: Host entry scan history not sorted.")
   409  			}
   410  			// Ignore the unsorted scan entry.
   411  			continue
   412  		}
   413  		if recentSuccess {
   414  			uptime += scan.Timestamp.Sub(recentTime)
   415  		} else {
   416  			downtime += scan.Timestamp.Sub(recentTime)
   417  		}
   418  		recentTime = scan.Timestamp
   419  		recentSuccess = scan.Success
   420  	}
   421  	// Sanity check against 0 total time.
   422  	if uptime == 0 && downtime == 0 {
   423  		return 0.001 // Shouldn't happen.
   424  	}
   425  
   426  	// Compute the uptime ratio, but shift by 0.02 to acknowledge fully that
   427  	// 98% uptime and 100% uptime is valued the same.
   428  	uptimeRatio := float64(uptime) / float64(uptime+downtime)
   429  	if uptimeRatio > 0.98 {
   430  		uptimeRatio = 0.98
   431  	}
   432  	uptimeRatio += 0.02
   433  
   434  	// Cap the total amount of downtime allowed based on the total number of
   435  	// scans that have happened.
   436  	allowedDowntime := 0.03 * float64(len(entry.ScanHistory))
   437  	if uptimeRatio < 1-allowedDowntime {
   438  		uptimeRatio = 1 - allowedDowntime
   439  	}
   440  
   441  	// Calculate the penalty for low uptime. Penalties increase extremely
   442  	// quickly as uptime falls away from 95%.
   443  	//
   444  	// 100% uptime = 1
   445  	// 98%  uptime = 1
   446  	// 95%  uptime = 0.83
   447  	// 90%  uptime = 0.26
   448  	// 85%  uptime = 0.03
   449  	// 80%  uptime = 0.001
   450  	// 75%  uptime = 0.00001
   451  	// 70%  uptime = 0.0000001
   452  	exp := 200 * math.Min(1-uptimeRatio, 0.30)
   453  	return math.Pow(uptimeRatio, exp)
   454  }
   455  
   456  // calculateHostWeightFn creates a hosttree.WeightFunc given an Allowance.
   457  func (hdb *HostDB) calculateHostWeightFn(allowance modules.Allowance) hosttree.WeightFunc {
   458  	// TODO: Pass these in as input instead of using the defaults.
   459  	ug := modules.DefaultUsageGuideLines
   460  
   461  	return func(entry modules.HostDBEntry) types.Currency {
   462  		collateralReward := hdb.collateralAdjustments(entry, allowance, ug)
   463  		interactionPenalty := hdb.interactionAdjustments(entry)
   464  		lifetimePenalty := hdb.lifetimeAdjustments(entry)
   465  		pricePenalty := hdb.priceAdjustments(entry, allowance, ug)
   466  		storageRemainingPenalty := storageRemainingAdjustments(entry)
   467  		uptimePenalty := hdb.uptimeAdjustments(entry)
   468  		versionPenalty := versionAdjustments(entry)
   469  
   470  		// Combine the adjustments.
   471  		fullPenalty := collateralReward * interactionPenalty * lifetimePenalty *
   472  			pricePenalty * storageRemainingPenalty * uptimePenalty * versionPenalty
   473  
   474  		// Return a types.Currency.
   475  		weight := baseWeight.MulFloat(fullPenalty)
   476  		if weight.IsZero() {
   477  			// A weight of zero is problematic for for the host tree.
   478  			return types.NewCurrency64(1)
   479  		}
   480  		return weight
   481  	}
   482  }
   483  
   484  // calculateConversionRate calculates the conversion rate of the provided
   485  // host score, comparing it to the hosts in the database and returning what
   486  // percentage of contracts it is likely to participate in.
   487  func (hdb *HostDB) calculateConversionRate(score types.Currency) float64 {
   488  	var totalScore types.Currency
   489  	for _, h := range hdb.ActiveHosts() {
   490  		totalScore = totalScore.Add(hdb.weightFunc(h))
   491  	}
   492  	if totalScore.IsZero() {
   493  		totalScore = types.NewCurrency64(1)
   494  	}
   495  	conversionRate, _ := big.NewRat(0, 1).SetFrac(score.Mul64(50).Big(), totalScore.Big()).Float64()
   496  	if conversionRate > 100 {
   497  		conversionRate = 100
   498  	}
   499  	return conversionRate
   500  }
   501  
   502  // EstimateHostScore takes a HostExternalSettings and returns the estimated
   503  // score of that host in the hostdb, assuming no penalties for age or uptime.
   504  func (hdb *HostDB) EstimateHostScore(entry modules.HostDBEntry, allowance modules.Allowance) modules.HostScoreBreakdown {
   505  	// TODO: Pass these in as input instead of using the defaults.
   506  	ug := modules.DefaultUsageGuideLines
   507  
   508  	// Grab the adjustments. Age, and uptime penalties are set to '1', to
   509  	// assume best behavior from the host.
   510  	collateralReward := hdb.collateralAdjustments(entry, allowance, ug)
   511  	pricePenalty := hdb.priceAdjustments(entry, allowance, ug)
   512  	storageRemainingPenalty := storageRemainingAdjustments(entry)
   513  	versionPenalty := versionAdjustments(entry)
   514  
   515  	// Combine into a full penalty, then determine the resulting estimated
   516  	// score.
   517  	fullPenalty := collateralReward * pricePenalty * storageRemainingPenalty * versionPenalty
   518  	estimatedScore := baseWeight.MulFloat(fullPenalty)
   519  	if estimatedScore.IsZero() {
   520  		estimatedScore = types.NewCurrency64(1)
   521  	}
   522  
   523  	// Compile the estimates into a host score breakdown.
   524  	return modules.HostScoreBreakdown{
   525  		Score:          estimatedScore,
   526  		ConversionRate: hdb.calculateConversionRate(estimatedScore),
   527  
   528  		AgeAdjustment:              1,
   529  		BurnAdjustment:             1,
   530  		CollateralAdjustment:       collateralReward,
   531  		PriceAdjustment:            pricePenalty,
   532  		StorageRemainingAdjustment: storageRemainingPenalty,
   533  		UptimeAdjustment:           1,
   534  		VersionAdjustment:          versionPenalty,
   535  	}
   536  }
   537  
   538  // ScoreBreakdown provdes a detailed set of scalars and bools indicating
   539  // elements of the host's overall score.
   540  func (hdb *HostDB) ScoreBreakdown(entry modules.HostDBEntry) modules.HostScoreBreakdown {
   541  	// TODO: Pass these in as input instead of using the defaults.
   542  	ug := modules.DefaultUsageGuideLines
   543  
   544  	hdb.mu.Lock()
   545  	defer hdb.mu.Unlock()
   546  
   547  	score := hdb.weightFunc(entry)
   548  	return modules.HostScoreBreakdown{
   549  		Score:          score,
   550  		ConversionRate: hdb.calculateConversionRate(score),
   551  
   552  		AgeAdjustment:              hdb.lifetimeAdjustments(entry),
   553  		BurnAdjustment:             1,
   554  		CollateralAdjustment:       hdb.collateralAdjustments(entry, hdb.allowance, ug),
   555  		InteractionAdjustment:      hdb.interactionAdjustments(entry),
   556  		PriceAdjustment:            hdb.priceAdjustments(entry, hdb.allowance, ug),
   557  		StorageRemainingAdjustment: storageRemainingAdjustments(entry),
   558  		UptimeAdjustment:           hdb.uptimeAdjustments(entry),
   559  		VersionAdjustment:          versionAdjustments(entry),
   560  	}
   561  }