Commit 92dbe06e authored by Luke Champine's avatar Luke Champine

replace conversion functions with conversion factor

parent e17d90b9
......@@ -9,6 +9,14 @@ const (
HostDir = "host"
)
var (
// BytesPerTerabyte is conversion rate between bytes and terabytes.
BytesPerTerabyte = types.NewCurrency64(1e12)
// BlockBytesPerMonthTerabyte is the conversion rate between block-bytes and month-TB.
BlockBytesPerMonthTerabyte = BytesPerTerabyte.Mul(types.NewCurrency64(4320))
)
type (
// HostFinancialMetrics provides financial statistics for the host,
// including money that is locked in contracts. Though verbose, these
......@@ -111,46 +119,3 @@ type (
StorageManager
}
)
// BandwidthPriceToConsensus converts a human bandwidth price, having the unit
// 'Siacoins per Terabyte', to a consensus storage price, having the unit
// 'Hastings per Byte'.
func BandwidthPriceToConsensus(siacoinsTB uint64) (hastingsByte types.Currency) {
hastingsTB := types.NewCurrency64(siacoinsTB).Mul(types.SiacoinPrecision)
return hastingsTB.Div(types.NewCurrency64(1e12))
}
// BandwidthPriceToHuman converts a consensus bandwidth price, having the unit
// 'Hastings per Byte' to a human bandwidth price, having the unit 'Siacoins
// per Terabyte'.
func BandwidthPriceToHuman(hastingsByte types.Currency) (siacoinsTB uint64, err error) {
hastingsTB := hastingsByte.Mul(types.NewCurrency64(1e12))
if hastingsTB.Cmp(types.SiacoinPrecision.Div(types.NewCurrency64(2))) < 0 {
// The result of the final division is going to be less than 0.5,
// therefore 0 should be returned.
return 0, nil
}
if hastingsTB.Cmp(types.SiacoinPrecision) < 0 {
// The result of the final division is going to be greater than or
// equal to 0.5, but less than 1, therefore 1 should be returned.
return 1, nil
}
return hastingsTB.Div(types.SiacoinPrecision).Uint64()
}
// StoragePriceToConsensus converts a human storage price, having the unit
// 'Siacoins per Month per Terabyte', to a consensus storage price, having the
// unit 'Hastings per Block per Byte'.
func StoragePriceToConsensus(siacoinsMonthTB uint64) (hastingsBlockByte types.Currency) {
// Perform multiplication first to preserve precision.
hastingsMonthTB := types.NewCurrency64(siacoinsMonthTB).Mul(types.SiacoinPrecision)
hastingsBlockTB := hastingsMonthTB.Div(types.NewCurrency64(4320))
return hastingsBlockTB.Div(types.NewCurrency64(1e12))
}
// StoragePriceToHuman converts a consensus storage price, having the unit
// 'Hastings per Block per Byte', to a human storage price, having the unit
// 'Hastings per Month per Terabyte'.
func StoragePriceToHuman(hastingsBlockByte types.Currency) (hastingsMonthTB types.Currency) {
return hastingsBlockByte.Mul(types.NewCurrency64(4320)).Mul(types.NewCurrency64(1e12))
}
......@@ -71,7 +71,7 @@ var (
// defaultDownloadBandwidthPrice defines the default price of upload
// bandwidth. The default is set to 10 siacoins per gigabyte, because
// download bandwidth is expected to be plentiful but also in-demand.
defaultDownloadBandwidthPrice = modules.BandwidthPriceToConsensus(10e3) // 10 SC / GB
defaultDownloadBandwidthPrice = types.NewCurrency64(10e3).Mul(types.SiacoinPrecision).Div(modules.BytesPerTerabyte) // 10k SC / TB
// defaultMaxDownloadBatchSize defines the maximum number of bytes that the
// host will allow to be requested by a single download request. 17 MiB has
......@@ -103,7 +103,7 @@ var (
// defaultStoragePrice defines the starting price for hosts selling
// storage. We try to match a number that is both reasonably profitable and
// reasonably competitive.
defaultStoragePrice = modules.StoragePriceToConsensus(20e3) // 20 SC / GB / Month
defaultStoragePrice = types.NewCurrency64(20e3).Mul(types.SiacoinPrecision).Div(modules.BlockBytesPerMonthTerabyte) // 20k SC / TB / Month
// defaultUploadBandwidthPrice defines the default price of upload
// bandwidth. The default is set to 1 siacoin per GB, because the host is
......@@ -111,7 +111,7 @@ var (
// the host is typically only downloading data if it is planning to store
// the data, meaning that the host serves to profit from accepting the
// data.
defaultUploadBandwidthPrice = modules.BandwidthPriceToConsensus(1e3) // 1 SC / GB
defaultUploadBandwidthPrice = types.NewCurrency64(1e3).Mul(types.SiacoinPrecision).Div(modules.BytesPerTerabyte) // 1 SC / TB
// defaultWindowSize is the size of the proof of storage window requested
// by the host. The host will not delete any obligations until the window
......
package modules
import (
"math"
"testing"
"github.com/NebulousLabs/Sia/types"
)
// TestUnitMaxFileContractSetLenSanity checks that a sensible value for
......@@ -23,158 +20,3 @@ func TestUnitMaxFileContractSetLenSanity(t *testing.T) {
}
}
// TestUnitStoragePriceConversions checks the functions StoragePriceToHuman and
// StoragePriceToConsensus, verifying that they correctly convert between
// human-readable prices and consensus-level prices.
func TestUnitStoragePriceConversions(t *testing.T) {
t.Parallel()
// Establish a series of trials for conversion.
trials := []struct {
consensus types.Currency
human uint64
}{
{
// Convert 0.
types.ZeroCurrency,
0,
},
{
// Convert from 1e12 - simple result.
types.NewCurrency64(1e12),
4320,
},
{
// Convert from a tiny human number.
types.NewCurrency64(6250e6),
27,
},
{
// Convert from types.SiacoinPrecision - simple result.
types.SiacoinPrecision,
4320e12,
},
}
// Run all of the trials.
for i, trial := range trials {
// Convert from the consensus result to the human result, and
// vice-versa. The transformations should be commutative, so both
// passing should indicate that the trial has succeeded.
toHuman, err := StoragePriceToHuman(trial.consensus)
if err != nil {
t.Error(i, err)
}
if toHuman != trial.human {
t.Error("StoragePriceToHuman conversion failed:", i, trial.consensus, trial.human, toHuman)
}
if StoragePriceToConsensus(trial.human).Cmp(trial.consensus) != 0 {
t.Error("StoragePriceToConsensus conversion failed:", i, trial.human, trial.consensus, StoragePriceToConsensus(trial.human))
}
}
// Check the corner cases for StoragePriceToHuman - rounding to 1, rounding
// to 0, and overflowing.
causeRoundToZero := types.NewCurrency64(115740740)
causeRoundToOne := types.NewCurrency64(115740741)
notOverflow := types.NewCurrency64(math.MaxUint64).Mul(types.NewCurrency64(231481481))
causeOverflow := types.NewCurrency64(math.MaxUint64).Mul(types.NewCurrency64(231481482))
toHuman, err := StoragePriceToHuman(causeRoundToZero)
if err != nil {
t.Error(err)
}
if toHuman != 0 {
t.Error("rounding is not happening correctly in StoragePriceToHuman")
}
toHuman, err = StoragePriceToHuman(causeRoundToOne)
if err != nil {
t.Error(err)
}
if toHuman != 1 {
t.Error("rounding is not happening correctly in StoragePriceToHuman")
}
_, err = StoragePriceToHuman(notOverflow)
if err != nil {
t.Error(err)
}
_, err = StoragePriceToHuman(causeOverflow)
if err != types.ErrUint64Overflow {
t.Error(err)
}
}
// TestUnitBandwidthPriceConversions checks the functions BandwidthPriceToHuman
// and BandwidthPriceToConsensus, verifying that they correctly convert between
// human-readable prices and consensus-level prices.
func TestUnitBandwidthPriceConversions(t *testing.T) {
t.Parallel()
// Establish a series of trials for conversion.
trials := []struct {
consensus types.Currency
human uint64
}{
{
// Convert 0.
types.ZeroCurrency,
0,
},
{
// Convert from 1e12 - simple result.
types.NewCurrency64(1e12),
1,
},
{
// Convert from types.SiacoinPrecision - simple result.
types.SiacoinPrecision,
1e12,
},
}
// Run the trials.
for i, trial := range trials {
// Convert from the consensus result to the human result, and
// vice-versa. The transformations should be commutative, so both
// passing should indicate that the trial has succeeded.
toHuman, err := BandwidthPriceToHuman(trial.consensus)
if err != nil {
t.Error(i, err)
}
if toHuman != trial.human {
t.Error("BandwidthPriceToHuman conversion failed:", i, trial.consensus, trial.human, toHuman)
}
if BandwidthPriceToConsensus(trial.human).Cmp(trial.consensus) != 0 {
t.Error("BandwidthPriceToConsensus conversion failed:", i, trial.human, trial.consensus, BandwidthPriceToConsensus(trial.human))
}
}
// Check the corner cases for StoragePriceToHuman - rounding to 1, rounding
// to 0, and overflowing.
causeRoundToZero := types.NewCurrency64(500e9 - 1)
causeRoundToOne := types.NewCurrency64(500e9)
notOverflow := types.NewCurrency64(math.MaxUint64).Mul(types.NewCurrency64(1e12))
causeOverflow := types.NewCurrency64(math.MaxUint64).Mul(types.NewCurrency64(1e12 + 1))
toHuman, err := BandwidthPriceToHuman(causeRoundToZero)
if err != nil {
t.Error(err)
}
if toHuman != 0 {
t.Error("rounding is not happening correctly in StoragePriceToHuman")
}
toHuman, err = BandwidthPriceToHuman(causeRoundToOne)
if err != nil {
t.Error(err)
}
if toHuman != 1 {
t.Error("rounding is not happening correctly in StoragePriceToHuman")
}
toHuman, err = BandwidthPriceToHuman(notOverflow)
if err != nil {
t.Error(err)
}
_, err = BandwidthPriceToHuman(causeOverflow)
if err != types.ErrUint64Overflow {
t.Error(err)
}
}
......@@ -19,7 +19,7 @@ const (
var (
// the contractor will not form contracts above this price
maxPrice = modules.StoragePriceToConsensus(500e3) // 500k SC / TB / Month
maxPrice = types.NewCurrency64(500e3).Mul(types.SiacoinPrecision).Mul(modules.BlockBytesPerMonthTerabyte) // 500k SC / TB / Month
errInsufficientAllowance = errors.New("allowance is not large enough to perform contract creation")
errSmallCollateral = errors.New("host collateral was too small")
......
......@@ -9,6 +9,7 @@ import (
"github.com/NebulousLabs/Sia/api"
"github.com/NebulousLabs/Sia/modules"
"github.com/NebulousLabs/Sia/types"
"github.com/spf13/cobra"
)
......@@ -142,7 +143,7 @@ func hostcmd() {
// convert accepting bool
accept := yesNo(is.AcceptingContracts)
// convert price from bytes/block to TB/Month
price := currencyUnits(modules.StoragePriceToHuman(is.MinimumStoragePrice))
price := currencyUnits(is.MinimumStoragePrice.Mul(modules.BlockBytesPerMonthTerabyte))
// calculate total revenue
totalRevenue := fm.ContractCompensation.
Add(fm.StorageRevenue).
......@@ -227,8 +228,8 @@ func hostconfigcmd(param, value string) {
die("Could not parse "+param+":", err)
}
i, _ := new(big.Int).SetString(hastings, 10)
i.Div(i, big.NewInt(1e12)) // divide by 1e12 bytes/TB
value = i.String()
c := types.NewCurrency(i).Div(modules.BytesPerTerabyte)
value = c.String()
// currency/TB/month (convert to hastings/byte/block)
case "minimumstorageprice":
......@@ -237,9 +238,8 @@ func hostconfigcmd(param, value string) {
die("Could not parse "+param+":", err)
}
i, _ := new(big.Int).SetString(hastings, 10)
i.Div(i, big.NewInt(1e12)) // divide by 1e12 bytes/TB
i.Div(i, big.NewInt(4320)) // divide by 4320 blocks/month
value = i.String()
c := types.NewCurrency(i).Div(modules.BlockBytesPerMonthTerabyte)
value = c.String()
// other valid settings
case "acceptingcontracts", "maxdownloadbatchsize", "maxduration",
......
......@@ -30,7 +30,7 @@ func hostdbcmd() {
}
fmt.Println("Active hosts:")
for _, host := range info.Hosts {
price := modules.StoragePriceToHuman(host.StoragePrice)
price := host.StoragePrice.Mul(modules.BlockBytesPerMonthTerabyte)
fmt.Printf("\t%v - %v / TB / Month\n", host.NetAddress, currencyUnits(price))
}
}
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