<commit_before>package hostdb

import (
	"math"
	"math/big"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

var (
	// Because most weights would otherwise be fractional, we set the base
	// weight to be very large.
	baseWeight = types.NewCurrency(new(big.Int).Exp(big.NewInt(10), big.NewInt(80), nil))

	// tbMonth is the number of bytes in a terabyte times the number of blocks
	// in a month.
	tbMonth = uint64(4032) * uint64(1e12)

	// collateralExponentiation is the number of times that the collateral is
	// multiplied into the price.
	collateralExponentiation = 1

	// priceDiveNormalization reduces the raw value of the price so that not so
	// many digits are needed when operating on the weight. This also allows the
	// base weight to be a lot lower.
	priceDivNormalization = types.SiacoinPrecision.Div64(10e3).Div64(tbMonth)

	// minCollateral is the amount of collateral we weight all hosts as having,
	// even if they do not have any collateral. This is to temporarily prop up
	// weak / cheap hosts on the network while the network is bootstrapping.
	minCollateral = types.SiacoinPrecision.Mul64(25).Div64(tbMonth)

	// Set a mimimum price, below which setting lower prices will no longer put
	// this host at an advatnage. This price is considered the bar for
	// 'essentially free', and is kept to a minimum to prevent certain Sybil
	// attack related attack vectors.
	//
	// NOTE: This needs to be intelligently adjusted down as the practical price
	// of storage changes, and as the price of the siacoin changes.
	minTotalPrice = types.SiacoinPrecision.Mul64(250).Div64(tbMonth)

	// priceExponentiation is the number of times that the weight is divided by
	// the price.
	priceExponentiation = 5

	// requiredStorage indicates the amount of storage that the host must be
	// offering in order to be considered a valuable/worthwhile host.
	requiredStorage = func() uint64 {
		switch build.Release {
		case "dev":
			return 1e6
		case "standard":
			return 5e9
		case "testing":
			return 1e3
		default:
			panic("incorrect/missing value for requiredStorage constant")
		}
	}()
)

// collateralAdjustments improves the host's weight according to the amount of
// collateral that they have provided.
func (hdb *HostDB) collateralAdjustments(entry modules.HostDBEntry) float64 {
	// Sanity checks - the constants values need to have certain relationships
	// to eachother
	if build.DEBUG {
		// If the minTotalPrice is not much larger than the divNormalization,
		// there will be problems with granularity after the divNormalization is
		// applied.
		if minCollateral.Div64(1e3).Cmp(priceDivNormalization) < 0 {
			build.Critical("Maladjusted minCollateral and divNormalization constants in hostdb package")
		}
	}

	// Set a minimum on the collateral, then normalize to a sane precision.
	usedCollateral := entry.Collateral
	if entry.Collateral.Cmp(minCollateral) < 0 {
		usedCollateral = minCollateral
	}
	baseU64, err := minCollateral.Div(priceDivNormalization).Uint64()
	if err != nil {
		baseU64 = math.MaxUint64
	}
	actualU64, err := usedCollateral.Div(priceDivNormalization).Uint64()
	if err != nil {
		actualU64 = math.MaxUint64
	}
	base := float64(baseU64)
	actual := float64(actualU64)

	// Exponentiate the results.
	weight := float64(1)
	for i := 0; i < collateralExponentiation; i++ {
		weight *= actual / base
	}
	return weight
}

// priceAdjustments will adjust the weight of the entry according to the prices
// that it has set.
func (hdb *HostDB) priceAdjustments(entry modules.HostDBEntry) float64 {
	// Sanity checks - the constants values need to have certain relationships
	// to eachother
	if build.DEBUG {
		// If the minTotalPrice is not much larger than the divNormalization,
		// there will be problems with granularity after the divNormalization is
		// applied.
		if minTotalPrice.Div64(1e3).Cmp(priceDivNormalization) < 0 {
			build.Critical("Maladjusted minDivePrice and divNormalization constants in hostdb package")
		}
	}

	// Prices tiered as follows:
	//    - the storage price is presented as 'per block per byte'
	//    - the contract price is presented as a flat rate
	//    - the upload bandwidth price is per byte
	//    - the download bandwidth price is per byte
	//
	// The hostdb will naively assume the following for now:
	//    - each contract covers 6 weeks of storage (default is 12 weeks, but
	//      renewals occur at midpoint) - 6048 blocks - and 25GB of storage.
	//    - uploads happen once per 12 weeks (average lifetime of a file is 12 weeks)
	//    - downloads happen once per 12 weeks (files are on average downloaded once throughout lifetime)
	//
	// In the future, the renter should be able to track average user behavior
	// and adjust accordingly. This flexibility will be added later.
	adjustedContractPrice := entry.ContractPrice.Div64(6048).Div64(25e9)        // Adjust contract price to match 25GB for 6 weeks.
	adjustedUploadPrice := entry.UploadBandwidthPrice.Div64(24192)              // Adjust upload price to match a single upload over 24 weeks.
	adjustedDownloadPrice := entry.DownloadBandwidthPrice.Div64(12096).Div64(3) // Adjust download price to match one download over 12 weeks, 1 redundancy.
	siafundFee := adjustedContractPrice.Add(adjustedUploadPrice).Add(adjustedDownloadPrice).Add(entry.Collateral).MulTax()
	totalPrice := entry.StoragePrice.Add(adjustedContractPrice).Add(adjustedUploadPrice).Add(adjustedDownloadPrice).Add(siafundFee)

	// Set a minimum on the price, then normalize to a sane precision.
	if totalPrice.Cmp(minTotalPrice) < 0 {
		totalPrice = minTotalPrice
	}
	baseU64, err := minTotalPrice.Div(priceDivNormalization).Uint64()
	if err != nil {
		baseU64 = math.MaxUint64
	}
	actualU64, err := totalPrice.Div(priceDivNormalization).Uint64()
	if err != nil {
		actualU64 = math.MaxUint64
	}
	base := float64(baseU64)
	actual := float64(actualU64)

	weight := float64(1)
	for i := 0; i < priceExponentiation; i++ {
		weight *= base / actual
	}
	return weight
}

// storageRemainingAdjustments adjusts the weight of the entry according to how
// much storage it has remaining.
func storageRemainingAdjustments(entry modules.HostDBEntry) float64 {
	base := float64(1)
	if entry.RemainingStorage < 200*requiredStorage {
		base = base / 2 // 2x total penalty
	}
	if entry.RemainingStorage < 50*requiredStorage {
		base = base / 3 // 6x total penalty
	}
	if entry.RemainingStorage < 25*requiredStorage {
		base = base / 4 // 24x total penalty
	}
	if entry.RemainingStorage < 10*requiredStorage {
		base = base / 5 // 96x total penalty
	}
	if entry.RemainingStorage < 5*requiredStorage {
		base = base / 10 // 960x total penalty
	}
	if entry.RemainingStorage < requiredStorage {
		base = base / 10 // 9600x total penalty
	}
	return base
}

// versionAdjustments will adjust the weight of the entry according to the siad
// version reported by the host.
func versionAdjustments(entry modules.HostDBEntry) float64 {
	base := float64(1)
	if build.VersionCmp(entry.Version, "1.1.1") < 0 {
		base = base / 5 // 5x total penalty.
	}
	if build.VersionCmp(entry.Version, "1.0.3") < 0 {
		base = base / 5 // 25x total penalty.
	}
	if build.VersionCmp(entry.Version, "1.0.0") < 0 {
		base = base / 20 // 500x total penalty.
	}
	return base
}

// lifetimeAdjustments will adjust the weight of the host according to the total<commit_msg>more heavily penalize hosts with little storage available<commit_after>package hostdb

import (
	"math"
	"math/big"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

var (
	// Because most weights would otherwise be fractional, we set the base
	// weight to be very large.
	baseWeight = types.NewCurrency(new(big.Int).Exp(big.NewInt(10), big.NewInt(80), nil))

	// tbMonth is the number of bytes in a terabyte times the number of blocks
	// in a month.
	tbMonth = uint64(4032) * uint64(1e12)

	// collateralExponentiation is the number of times that the collateral is
	// multiplied into the price.
	collateralExponentiation = 1

	// priceDiveNormalization reduces the raw value of the price so that not so
	// many digits are needed when operating on the weight. This also allows the
	// base weight to be a lot lower.
	priceDivNormalization = types.SiacoinPrecision.Div64(10e3).Div64(tbMonth)

	// minCollateral is the amount of collateral we weight all hosts as having,
	// even if they do not have any collateral. This is to temporarily prop up
	// weak / cheap hosts on the network while the network is bootstrapping.
	minCollateral = types.SiacoinPrecision.Mul64(25).Div64(tbMonth)

	// Set a mimimum price, below which setting lower prices will no longer put
	// this host at an advatnage. This price is considered the bar for
	// 'essentially free', and is kept to a minimum to prevent certain Sybil
	// attack related attack vectors.
	//
	// NOTE: This needs to be intelligently adjusted down as the practical price
	// of storage changes, and as the price of the siacoin changes.
	minTotalPrice = types.SiacoinPrecision.Mul64(250).Div64(tbMonth)

	// priceExponentiation is the number of times that the weight is divided by
	// the price.
	priceExponentiation = 5

	// requiredStorage indicates the amount of storage that the host must be
	// offering in order to be considered a valuable/worthwhile host.
	requiredStorage = build.Select(build.Var{
		Standard: uint64(20e9),
		Dev:      uint64(1e6),
		Testing:  uint64(1e3),
	}).(uint64)
)

// collateralAdjustments improves the host's weight according to the amount of
// collateral that they have provided.
func (hdb *HostDB) collateralAdjustments(entry modules.HostDBEntry) float64 {
	// Sanity checks - the constants values need to have certain relationships
	// to eachother
	if build.DEBUG {
		// If the minTotalPrice is not much larger than the divNormalization,
		// there will be problems with granularity after the divNormalization is
		// applied.
		if minCollateral.Div64(1e3).Cmp(priceDivNormalization) < 0 {
			build.Critical("Maladjusted minCollateral and divNormalization constants in hostdb package")
		}
	}

	// Set a minimum on the collateral, then normalize to a sane precision.
	usedCollateral := entry.Collateral
	if entry.Collateral.Cmp(minCollateral) < 0 {
		usedCollateral = minCollateral
	}
	baseU64, err := minCollateral.Div(priceDivNormalization).Uint64()
	if err != nil {
		baseU64 = math.MaxUint64
	}
	actualU64, err := usedCollateral.Div(priceDivNormalization).Uint64()
	if err != nil {
		actualU64 = math.MaxUint64
	}
	base := float64(baseU64)
	actual := float64(actualU64)

	// Exponentiate the results.
	weight := float64(1)
	for i := 0; i < collateralExponentiation; i++ {
		weight *= actual / base
	}
	return weight
}

// priceAdjustments will adjust the weight of the entry according to the prices
// that it has set.
func (hdb *HostDB) priceAdjustments(entry modules.HostDBEntry) float64 {
	// Sanity checks - the constants values need to have certain relationships
	// to eachother
	if build.DEBUG {
		// If the minTotalPrice is not much larger than the divNormalization,
		// there will be problems with granularity after the divNormalization is
		// applied.
		if minTotalPrice.Div64(1e3).Cmp(priceDivNormalization) < 0 {
			build.Critical("Maladjusted minDivePrice and divNormalization constants in hostdb package")
		}
	}

	// Prices tiered as follows:
	//    - the storage price is presented as 'per block per byte'
	//    - the contract price is presented as a flat rate
	//    - the upload bandwidth price is per byte
	//    - the download bandwidth price is per byte
	//
	// The hostdb will naively assume the following for now:
	//    - each contract covers 6 weeks of storage (default is 12 weeks, but
	//      renewals occur at midpoint) - 6048 blocks - and 25GB of storage.
	//    - uploads happen once per 12 weeks (average lifetime of a file is 12 weeks)
	//    - downloads happen once per 12 weeks (files are on average downloaded once throughout lifetime)
	//
	// In the future, the renter should be able to track average user behavior
	// and adjust accordingly. This flexibility will be added later.
	adjustedContractPrice := entry.ContractPrice.Div64(6048).Div64(25e9)        // Adjust contract price to match 25GB for 6 weeks.
	adjustedUploadPrice := entry.UploadBandwidthPrice.Div64(24192)              // Adjust upload price to match a single upload over 24 weeks.
	adjustedDownloadPrice := entry.DownloadBandwidthPrice.Div64(12096).Div64(3) // Adjust download price to match one download over 12 weeks, 1 redundancy.
	siafundFee := adjustedContractPrice.Add(adjustedUploadPrice).Add(adjustedDownloadPrice).Add(entry.Collateral).MulTax()
	totalPrice := entry.StoragePrice.Add(adjustedContractPrice).Add(adjustedUploadPrice).Add(adjustedDownloadPrice).Add(siafundFee)

	// Set a minimum on the price, then normalize to a sane precision.
	if totalPrice.Cmp(minTotalPrice) < 0 {
		totalPrice = minTotalPrice
	}
	baseU64, err := minTotalPrice.Div(priceDivNormalization).Uint64()
	if err != nil {
		baseU64 = math.MaxUint64
	}
	actualU64, err := totalPrice.Div(priceDivNormalization).Uint64()
	if err != nil {
		actualU64 = math.MaxUint64
	}
	base := float64(baseU64)
	actual := float64(actualU64)

	weight := float64(1)
	for i := 0; i < priceExponentiation; i++ {
		weight *= base / actual
	}
	return weight
}

// storageRemainingAdjustments adjusts the weight of the entry according to how
// much storage it has remaining.
func storageRemainingAdjustments(entry modules.HostDBEntry) float64 {
	base := float64(1)
	if entry.RemainingStorage < 200*requiredStorage {
		base = base / 2 // 2x total penalty
	}
	if entry.RemainingStorage < 150*requiredStorage {
		base = base / 2 // 4x total penalty
	}
	if entry.RemainingStorage < 100*requiredStorage {
		base = base / 3 // 12x total penalty
	}
	if entry.RemainingStorage < 80*requiredStorage {
		base = base / 3 // 36x total penalty
	}
	if entry.RemainingStorage < 40*requiredStorage {
		base = base / 4 // 144x total penalty
	}
	if entry.RemainingStorage < 20*requiredStorage {
		base = base / 5 // 720x total penalty
	}
	if entry.RemainingStorage < 10*requiredStorage {
		base = base / 5 // 3,600x total penalty
	}
	if entry.RemainingStorage < 5*requiredStorage {
		base = base / 5 // 14,400x total penalty
	}
	if entry.RemainingStorage < requiredStorage {
		base = base / 5 // 72,000x total penalty
	}
	return base
}

// versionAdjustments will adjust the weight of the entry according to the siad
// version reported by the host.
func versionAdjustments(entry modules.HostDBEntry) float64 {
	base := float64(1)
	if build.VersionCmp(entry.Version, "1.1.1") < 0 {
		base = base / 5 // 5x total penalty.
	}
	if build.VersionCmp(entry.Version, "1.0.3") < 0 {
		base = base / 5 // 25x total penalty.
	}
	if build.VersionCmp(entry.Version, "1.0.0") < 0 {
		base = base / 20 // 500x total penalty.
	}
	return base
}

// lifetimeAdjustments will adjust the weight of the host according to the total
{'fraction_non_alphanumeric': 0.08790258184721852, 'fraction_numerical': 0.03267234495608198, 'mean_word_length': 3.6848503740648377, 'pattern_counts': {'":': 3, '<': 36, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3611277', 'n_tokens_mistral': 4978, 'n_tokens_neox': 4475, 'n_words': 1633}