github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/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 "errors" 12 "math" 13 "math/big" 14 15 "SiaPrime/build" 16 ) 17 18 type ( 19 // A Currency represents a number of siacoins or siafunds. Internally, a 20 // Currency value is unbounded; however, Currency values sent over the wire 21 // protocol are subject to a maximum size of 255 bytes (approximately 10^614). 22 // Unlike the math/big library, whose methods modify their receiver, all 23 // arithmetic Currency methods return a new value. Currency cannot be negative. 24 Currency struct { 25 i big.Int 26 } 27 ) 28 29 var ( 30 // ErrNegativeCurrency is the error that is returned if performing an 31 // operation results in a negative currency. 32 ErrNegativeCurrency = errors.New("negative currency not allowed") 33 34 // ErrUint64Overflow is the error that is returned if converting to a 35 // unit64 would cause an overflow. 36 ErrUint64Overflow = errors.New("cannot return the uint64 of this currency - result is an overflow") 37 38 // ZeroCurrency defines a currency of value zero. 39 ZeroCurrency = NewCurrency64(0) 40 ) 41 42 // NewCurrency creates a Currency value from a big.Int. Undefined behavior 43 // occurs if a negative input is used. 44 func NewCurrency(b *big.Int) (c Currency) { 45 if b.Sign() < 0 { 46 build.Critical(ErrNegativeCurrency) 47 } else { 48 c.i = *b 49 } 50 return 51 } 52 53 // NewCurrency64 creates a Currency value from a uint64. 54 func NewCurrency64(x uint64) (c Currency) { 55 c.i.SetUint64(x) 56 return 57 } 58 59 // Add returns a new Currency value c = x + y 60 func (x Currency) Add(y Currency) (c Currency) { 61 c.i.Add(&x.i, &y.i) 62 return 63 } 64 65 // Big returns the value of c as a *big.Int. Importantly, it does not provide 66 // access to the c's internal big.Int object, only a copy. 67 func (x Currency) Big() *big.Int { 68 return new(big.Int).Set(&x.i) 69 } 70 71 // Cmp compares two Currency values. The return value follows the convention 72 // of math/big. 73 func (x Currency) Cmp(y Currency) int { 74 return x.i.Cmp(&y.i) 75 } 76 77 // Cmp64 compares x to a uint64. The return value follows the convention of 78 // math/big. 79 func (x Currency) Cmp64(y uint64) int { 80 return x.Cmp(NewCurrency64(y)) 81 } 82 83 // Div returns a new Currency value c = x / y. 84 func (x Currency) Div(y Currency) (c Currency) { 85 c.i.Div(&x.i, &y.i) 86 return 87 } 88 89 // Div64 returns a new Currency value c = x / y. 90 func (x Currency) Div64(y uint64) (c Currency) { 91 c.i.Div(&x.i, new(big.Int).SetUint64(y)) 92 return 93 } 94 95 // Equals returns true if x and y have the same value. 96 func (x Currency) Equals(y Currency) bool { 97 return x.Cmp(y) == 0 98 } 99 100 // Equals64 returns true if x and y have the same value. 101 func (x Currency) Equals64(y uint64) bool { 102 return x.Cmp64(y) == 0 103 } 104 105 // Mul returns a new Currency value c = x * y. 106 func (x Currency) Mul(y Currency) (c Currency) { 107 c.i.Mul(&x.i, &y.i) 108 return 109 } 110 111 // Mul64 returns a new Currency value c = x * y. 112 func (x Currency) Mul64(y uint64) (c Currency) { 113 c.i.Mul(&x.i, new(big.Int).SetUint64(y)) 114 return 115 } 116 117 // COMPATv0.4.0 - until the first 10e3 blocks have been archived, MulFloat is 118 // needed while verifying the first set of blocks. 119 120 // MulFloat returns a new Currency value y = c * x, where x is a float64. 121 // Behavior is undefined when x is negative. 122 func (x Currency) MulFloat(y float64) (c Currency) { 123 if y < 0 { 124 build.Critical(ErrNegativeCurrency) 125 } else { 126 cRat := new(big.Rat).Mul( 127 new(big.Rat).SetInt(&x.i), 128 new(big.Rat).SetFloat64(y), 129 ) 130 c.i.Div(cRat.Num(), cRat.Denom()) 131 } 132 return 133 } 134 135 // MulRat returns a new Currency value c = x * y, where y is a big.Rat. 136 func (x Currency) MulRat(y *big.Rat) (c Currency) { 137 if y.Sign() < 0 { 138 build.Critical(ErrNegativeCurrency) 139 } else { 140 c.i.Mul(&x.i, y.Num()) 141 c.i.Div(&c.i, y.Denom()) 142 } 143 return 144 } 145 146 // MulTax returns a new Currency value c = x * 0.039, where 0.039 is a big.Rat. 147 func (x Currency) MulTax() (c Currency) { 148 c.i.Mul(&x.i, big.NewInt(39)) 149 c.i.Div(&c.i, big.NewInt(1000)) 150 return c 151 } 152 153 // RoundDown returns the largest multiple of y <= x. 154 func (x Currency) RoundDown(y Currency) (c Currency) { 155 diff := new(big.Int).Mod(&x.i, &y.i) 156 c.i.Sub(&x.i, diff) 157 return 158 } 159 160 // IsZero returns true if the value is 0, false otherwise. 161 func (x Currency) IsZero() bool { 162 return x.i.Sign() <= 0 163 } 164 165 // Sqrt returns a new Currency value y = sqrt(c). Result is rounded down to the 166 // nearest integer. 167 func (x Currency) Sqrt() (c Currency) { 168 f, _ := new(big.Rat).SetInt(&x.i).Float64() 169 sqrt := new(big.Rat).SetFloat64(math.Sqrt(f)) 170 c.i.Div(sqrt.Num(), sqrt.Denom()) 171 return 172 } 173 174 // Sub returns a new Currency value c = x - y. Behavior is undefined when 175 // x < y. 176 func (x Currency) Sub(y Currency) (c Currency) { 177 if x.Cmp(y) < 0 { 178 c = ZeroCurrency 179 build.Critical(ErrNegativeCurrency) 180 } else { 181 c.i.Sub(&x.i, &y.i) 182 } 183 return 184 } 185 186 // Uint64 converts a Currency to a uint64. An error is returned because this 187 // function is sometimes called on values that can be determined by users - 188 // rather than have all user-facing points do input checking, the input 189 // checking should happen at the base type. This minimizes the chances of a 190 // rogue user causing a build.Critical to be triggered. 191 func (x Currency) Uint64() (u uint64, err error) { 192 if x.Cmp(NewCurrency64(math.MaxUint64)) > 0 { 193 return 0, ErrUint64Overflow 194 } 195 return x.Big().Uint64(), nil 196 }