currency.go 5.62 KB
Newer Older
1
package types
2

David Vorick's avatar
David Vorick committed
3 4 5 6 7 8 9
// currency.go defines the internal currency object. One design goal of the
// currency type is immutability: the currency type should be safe to pass
// directly to other objects and packages. The currency object should never
// have a negative value. The currency should never overflow. There is a
// maximum size value that can be encoded (around 10^10^20), however exceeding
// this value will not result in overflow.

10
import (
David Vorick's avatar
David Vorick committed
11
	"errors"
12
	"math"
13
	"math/big"
David Vorick's avatar
David Vorick committed
14

15
	"gitlab.com/NebulousLabs/Sia/build"
16 17
)

David Vorick's avatar
David Vorick committed
18 19 20 21 22 23 24 25 26 27 28
type (
	// A Currency represents a number of siacoins or siafunds. Internally, a
	// Currency value is unbounded; however, Currency values sent over the wire
	// protocol are subject to a maximum size of 255 bytes (approximately 10^614).
	// Unlike the math/big library, whose methods modify their receiver, all
	// arithmetic Currency methods return a new value. Currency cannot be negative.
	Currency struct {
		i big.Int
	}
)

29
var (
30 31
	// ErrNegativeCurrency is the error that is returned if performing an
	// operation results in a negative currency.
32
	ErrNegativeCurrency = errors.New("negative currency not allowed")
33 34 35 36

	// ErrUint64Overflow is the error that is returned if converting to a
	// unit64 would cause an overflow.
	ErrUint64Overflow = errors.New("cannot return the uint64 of this currency - result is an overflow")
37 38 39

	// ZeroCurrency defines a currency of value zero.
	ZeroCurrency = NewCurrency64(0)
40 41
)

42 43
// NewCurrency creates a Currency value from a big.Int. Undefined behavior
// occurs if a negative input is used.
44
func NewCurrency(b *big.Int) (c Currency) {
David Vorick's avatar
David Vorick committed
45
	if b.Sign() < 0 {
46
		build.Critical(ErrNegativeCurrency)
David Vorick's avatar
David Vorick committed
47 48
	} else {
		c.i = *b
49
	}
50 51 52
	return
}

53
// NewCurrency64 creates a Currency value from a uint64.
54 55
func NewCurrency64(x uint64) (c Currency) {
	c.i.SetUint64(x)
56
	return
57 58
}

59 60 61
// Add returns a new Currency value c = x + y
func (x Currency) Add(y Currency) (c Currency) {
	c.i.Add(&x.i, &y.i)
David Vorick's avatar
David Vorick committed
62 63 64
	return
}

65
// Big returns the value of c as a *big.Int. Importantly, it does not provide
David Vorick's avatar
David Vorick committed
66
// access to the c's internal big.Int object, only a copy.
67 68
func (x Currency) Big() *big.Int {
	return new(big.Int).Set(&x.i)
69 70
}

71
// Cmp compares two Currency values. The return value follows the convention
David Vorick's avatar
David Vorick committed
72
// of math/big.
73 74
func (x Currency) Cmp(y Currency) int {
	return x.i.Cmp(&y.i)
75 76
}

Luke Champine's avatar
Luke Champine committed
77 78 79 80 81 82
// Cmp64 compares x to a uint64. The return value follows the convention of
// math/big.
func (x Currency) Cmp64(y uint64) int {
	return x.Cmp(NewCurrency64(y))
}

83 84 85
// Div returns a new Currency value c = x / y.
func (x Currency) Div(y Currency) (c Currency) {
	c.i.Div(&x.i, &y.i)
86
	return
87 88
}

Luke Champine's avatar
Luke Champine committed
89 90 91 92 93 94
// Div64 returns a new Currency value c = x / y.
func (x Currency) Div64(y uint64) (c Currency) {
	c.i.Div(&x.i, new(big.Int).SetUint64(y))
	return
}

Luke Champine's avatar
Luke Champine committed
95 96 97 98 99
// Equals returns true if x and y have the same value.
func (x Currency) Equals(y Currency) bool {
	return x.Cmp(y) == 0
}

100 101
// Float64 will return the types.Currency as a float64.
func (x Currency) Float64() (f64 float64, exact bool) {
102
	return new(big.Rat).SetInt(&x.i).Float64()
103 104
}

Luke Champine's avatar
Luke Champine committed
105 106 107 108 109
// Equals64 returns true if x and y have the same value.
func (x Currency) Equals64(y uint64) bool {
	return x.Cmp64(y) == 0
}

110 111 112
// Mul returns a new Currency value c = x * y.
func (x Currency) Mul(y Currency) (c Currency) {
	c.i.Mul(&x.i, &y.i)
113
	return
114 115
}

Luke Champine's avatar
Luke Champine committed
116 117 118 119 120 121
// Mul64 returns a new Currency value c = x * y.
func (x Currency) Mul64(y uint64) (c Currency) {
	c.i.Mul(&x.i, new(big.Int).SetUint64(y))
	return
}

122 123
// COMPATv0.4.0 - until the first 10e3 blocks have been archived, MulFloat is
// needed while verifying the first set of blocks.
124

125 126 127 128
// MulFloat returns a new Currency value y = c * x, where x is a float64.
// Behavior is undefined when x is negative.
func (x Currency) MulFloat(y float64) (c Currency) {
	if y < 0 {
129
		build.Critical(ErrNegativeCurrency)
130 131 132 133 134 135 136 137 138 139 140
	} else {
		cRat := new(big.Rat).Mul(
			new(big.Rat).SetInt(&x.i),
			new(big.Rat).SetFloat64(y),
		)
		c.i.Div(cRat.Num(), cRat.Denom())
	}
	return
}

// MulRat returns a new Currency value c = x * y, where y is a big.Rat.
David Vorick's avatar
David Vorick committed
141 142
func (x Currency) MulRat(y *big.Rat) (c Currency) {
	if y.Sign() < 0 {
143
		build.Critical(ErrNegativeCurrency)
144
	} else {
David Vorick's avatar
David Vorick committed
145 146
		c.i.Mul(&x.i, y.Num())
		c.i.Div(&c.i, y.Denom())
147
	}
148
	return
149 150
}

151 152 153 154 155 156 157
// MulTax returns a new Currency value c = x * 0.039, where 0.039 is a big.Rat.
func (x Currency) MulTax() (c Currency) {
	c.i.Mul(&x.i, big.NewInt(39))
	c.i.Div(&c.i, big.NewInt(1000))
	return c
}

158
// RoundDown returns the largest multiple of y <= x.
David Vorick's avatar
David Vorick committed
159 160
func (x Currency) RoundDown(y Currency) (c Currency) {
	diff := new(big.Int).Mod(&x.i, &y.i)
161
	c.i.Sub(&x.i, diff)
David Vorick's avatar
David Vorick committed
162 163 164
	return
}

David Vorick's avatar
David Vorick committed
165
// IsZero returns true if the value is 0, false otherwise.
166 167
func (x Currency) IsZero() bool {
	return x.i.Sign() <= 0
David Vorick's avatar
David Vorick committed
168 169
}

170 171
// Sqrt returns a new Currency value y = sqrt(c). Result is rounded down to the
// nearest integer.
172 173
func (x Currency) Sqrt() (c Currency) {
	f, _ := new(big.Rat).SetInt(&x.i).Float64()
174
	sqrt := new(big.Rat).SetFloat64(math.Sqrt(f))
175
	c.i.Div(sqrt.Num(), sqrt.Denom())
176
	return
177
}
178

179 180 181 182
// Sub returns a new Currency value c = x - y. Behavior is undefined when
// x < y.
func (x Currency) Sub(y Currency) (c Currency) {
	if x.Cmp(y) < 0 {
183
		c = ZeroCurrency
184
		build.Critical(ErrNegativeCurrency)
185
	} else {
186
		c.i.Sub(&x.i, &y.i)
187
	}
188
	return
189 190
}

191 192 193 194 195
// Uint64 converts a Currency to a uint64. An error is returned because this
// function is sometimes called on values that can be determined by users -
// rather than have all user-facing points do input checking, the input
// checking should happen at the base type. This minimizes the chances of a
// rogue user causing a build.Critical to be triggered.
196 197
func (x Currency) Uint64() (u uint64, err error) {
	if x.Cmp(NewCurrency64(math.MaxUint64)) > 0 {
198 199
		return 0, ErrUint64Overflow
	}
200
	return x.Big().Uint64(), nil
201
}