github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/types/currency.go (about) 1 package types 2 3 // currency.go defines the internal currency object. One design goal of the 4 // currency type is immutability: the currency type should be safe to pass 5 // directly to other objects and packages. The currency object should never 6 // have a negative value. The currency should never overflow. There is a 7 // maximum size value that can be encoded (around 10^10^20), however exceeding 8 // this value will not result in overflow. 9 10 import ( 11 "bytes" 12 "errors" 13 "fmt" 14 "io" 15 "math" 16 "math/big" 17 18 "github.com/NebulousLabs/Sia/build" 19 "github.com/NebulousLabs/Sia/encoding" 20 ) 21 22 type ( 23 // A Currency represents a number of siacoins or siafunds. Internally, a 24 // Currency value is unbounded; however, Currency values sent over the wire 25 // protocol are subject to a maximum size of 255 bytes (approximately 10^614). 26 // Unlike the math/big library, whose methods modify their receiver, all 27 // arithmetic Currency methods return a new value. Currency cannot be negative. 28 Currency struct { 29 i big.Int 30 } 31 ) 32 33 var ( 34 // ZeroCurrency defines a currency of value zero. 35 ZeroCurrency = NewCurrency64(0) 36 37 // ErrNegativeCurrency is the error that is returned if performing an 38 // operation results in a negative currency. 39 ErrNegativeCurrency = errors.New("negative currency not allowed") 40 41 // ErrUint64Overflow is the error that is returned if converting to a 42 // unit64 would cause an overflow. 43 ErrUint64Overflow = errors.New("cannot return the uint64 of this currency - result is an overflow") 44 ) 45 46 // NewCurrency creates a Currency value from a big.Int. Undefined behavior 47 // occurs if a negative input is used. 48 func NewCurrency(b *big.Int) (c Currency) { 49 if b.Sign() < 0 { 50 build.Critical(ErrNegativeCurrency) 51 } else { 52 c.i = *b 53 } 54 return 55 } 56 57 // NewCurrency64 creates a Currency value from a uint64. 58 func NewCurrency64(x uint64) (c Currency) { 59 c.i.SetUint64(x) 60 return 61 } 62 63 // Add returns a new Currency value c = x + y 64 func (x Currency) Add(y Currency) (c Currency) { 65 c.i.Add(&x.i, &y.i) 66 return 67 } 68 69 // Big returns the value of c as a *big.Int. Importantly, it does not provide 70 // access to the c's internal big.Int object, only a copy. 71 func (c Currency) Big() *big.Int { 72 return new(big.Int).Set(&c.i) 73 } 74 75 // Cmp compares two Currency values. The return value follows the convention 76 // of math/big. 77 func (x Currency) Cmp(y Currency) int { 78 return x.i.Cmp(&y.i) 79 } 80 81 // Div returns a new Currency value c = x / y. 82 func (x Currency) Div(y Currency) (c Currency) { 83 c.i.Div(&x.i, &y.i) 84 return 85 } 86 87 // Div64 returns a new Currency value c = x / y. 88 func (x Currency) Div64(y uint64) (c Currency) { 89 c.i.Div(&x.i, new(big.Int).SetUint64(y)) 90 return 91 } 92 93 // Mul returns a new Currency value c = x * y. 94 func (x Currency) Mul(y Currency) (c Currency) { 95 c.i.Mul(&x.i, &y.i) 96 return 97 } 98 99 // Mul64 returns a new Currency value c = x * y. 100 func (x Currency) Mul64(y uint64) (c Currency) { 101 c.i.Mul(&x.i, new(big.Int).SetUint64(y)) 102 return 103 } 104 105 // COMPATv0.4.0 - until the first 10e3 blocks have been archived, MulFloat is 106 // needed while verifying the first set of blocks. 107 // 108 // MulFloat returns a new Currency value y = c * x, where x is a float64. 109 // Behavior is undefined when x is negative. 110 func (x Currency) MulFloat(y float64) (c Currency) { 111 if y < 0 { 112 build.Critical(ErrNegativeCurrency) 113 } else { 114 cRat := new(big.Rat).Mul( 115 new(big.Rat).SetInt(&x.i), 116 new(big.Rat).SetFloat64(y), 117 ) 118 c.i.Div(cRat.Num(), cRat.Denom()) 119 } 120 return 121 } 122 123 // MulRat returns a new Currency value c = x * y, where y is a big.Rat. 124 func (x Currency) MulRat(y *big.Rat) (c Currency) { 125 if y.Sign() < 0 { 126 build.Critical(ErrNegativeCurrency) 127 } else { 128 c.i.Mul(&x.i, y.Num()) 129 c.i.Div(&c.i, y.Denom()) 130 } 131 return 132 } 133 134 // MulTax returns a new Currency value c = x * 0.039, where 0.039 is a big.Rat. 135 func (x Currency) MulTax() (c Currency) { 136 c.i.Mul(&x.i, big.NewInt(39)) 137 c.i.Div(&c.i, big.NewInt(1000)) 138 return c 139 } 140 141 // RoundDown returns the largest multiple of y <= x. 142 func (x Currency) RoundDown(y Currency) (c Currency) { 143 diff := new(big.Int).Mod(&x.i, &y.i) 144 c.i.Sub(&x.i, diff) 145 return 146 } 147 148 // IsZero returns true if the value is 0, false otherwise. 149 func (c Currency) IsZero() bool { 150 return c.i.Sign() <= 0 151 } 152 153 // Sqrt returns a new Currency value y = sqrt(c). Result is rounded down to the 154 // nearest integer. 155 func (x Currency) Sqrt() (c Currency) { 156 f, _ := new(big.Rat).SetInt(&x.i).Float64() 157 sqrt := new(big.Rat).SetFloat64(math.Sqrt(f)) 158 c.i.Div(sqrt.Num(), sqrt.Denom()) 159 return 160 } 161 162 // Sub returns a new Currency value c = x - y. Behavior is undefined when 163 // x < y. 164 func (x Currency) Sub(y Currency) (c Currency) { 165 if x.Cmp(y) < 0 { 166 c = x 167 build.Critical(ErrNegativeCurrency) 168 } else { 169 c.i.Sub(&x.i, &y.i) 170 } 171 return 172 } 173 174 // Uint64 converts a Currency to a uint64. An error is returned because this 175 // function is sometimes called on values that can be determined by users - 176 // rather than have all user-facing points do input checking, the input 177 // checking should happen at the base type. This minimizes the chances of a 178 // rogue user causing a build.Critical to be triggered. 179 func (c Currency) Uint64() (u uint64, err error) { 180 if c.Cmp(NewCurrency64(math.MaxUint64)) > 0 { 181 return 0, ErrUint64Overflow 182 } 183 return c.Big().Uint64(), nil 184 } 185 186 // MarshalJSON implements the json.Marshaler interface. 187 func (c Currency) MarshalJSON() ([]byte, error) { 188 // Must enclosed the value in quotes; otherwise JS will convert it to a 189 // double and lose precision. 190 return []byte(`"` + c.String() + `"`), nil 191 } 192 193 // UnmarshalJSON implements the json.Unmarshaler interface. An error is 194 // returned if a negative number is provided. 195 func (c *Currency) UnmarshalJSON(b []byte) error { 196 // UnmarshalJSON does not expect quotes 197 b = bytes.Trim(b, `"`) 198 err := c.i.UnmarshalJSON(b) 199 if err != nil { 200 return err 201 } 202 if c.i.Sign() < 0 { 203 c.i = *big.NewInt(0) 204 return ErrNegativeCurrency 205 } 206 return nil 207 } 208 209 // MarshalSia implements the encoding.SiaMarshaler interface. It writes the 210 // byte-slice representation of the Currency's internal big.Int to w. Note 211 // that as the bytes of the big.Int correspond to the absolute value of the 212 // integer, there is no way to marshal a negative Currency. 213 func (c Currency) MarshalSia(w io.Writer) error { 214 return encoding.WritePrefix(w, c.i.Bytes()) 215 } 216 217 // UnmarshalSia implements the encoding.SiaUnmarshaler interface. 218 func (c *Currency) UnmarshalSia(r io.Reader) error { 219 b, err := encoding.ReadPrefix(r, 256) 220 if err != nil { 221 return err 222 } 223 c.i.SetBytes(b) 224 return nil 225 } 226 227 // String implements the fmt.Stringer interface. 228 func (c Currency) String() string { 229 return c.i.String() 230 } 231 232 // Scan implements the fmt.Scanner interface, allowing Currency values to be 233 // scanned from text. 234 func (c *Currency) Scan(s fmt.ScanState, ch rune) error { 235 err := c.i.Scan(s, ch) 236 if err != nil { 237 return err 238 } 239 if c.i.Sign() < 0 { 240 return ErrNegativeCurrency 241 } 242 return nil 243 }