gitlab.com/SiaPrime/SiaPrime@v1.4.1/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 "gitlab.com/SiaPrime/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 // Float64 will return the types.Currency as a float64. 101 func (x Currency) Float64() (f64 float64, exact bool) { 102 return new(big.Rat).SetInt(&x.i).Float64() 103 } 104 105 // Equals64 returns true if x and y have the same value. 106 func (x Currency) Equals64(y uint64) bool { 107 return x.Cmp64(y) == 0 108 } 109 110 // Mul returns a new Currency value c = x * y. 111 func (x Currency) Mul(y Currency) (c Currency) { 112 c.i.Mul(&x.i, &y.i) 113 return 114 } 115 116 // Mul64 returns a new Currency value c = x * y. 117 func (x Currency) Mul64(y uint64) (c Currency) { 118 c.i.Mul(&x.i, new(big.Int).SetUint64(y)) 119 return 120 } 121 122 // COMPATv0.4.0 - until the first 10e3 blocks have been archived, MulFloat is 123 // needed while verifying the first set of blocks. 124 125 // MulFloat returns a new Currency value y = c * x, where x is a float64. 126 // Behavior is undefined when x is negative. 127 func (x Currency) MulFloat(y float64) (c Currency) { 128 if y < 0 { 129 build.Critical(ErrNegativeCurrency) 130 } else { 131 cRat := new(big.Rat).Mul( 132 new(big.Rat).SetInt(&x.i), 133 new(big.Rat).SetFloat64(y), 134 ) 135 c.i.Div(cRat.Num(), cRat.Denom()) 136 } 137 return 138 } 139 140 // MulRat returns a new Currency value c = x * y, where y is a big.Rat. 141 func (x Currency) MulRat(y *big.Rat) (c Currency) { 142 if y.Sign() < 0 { 143 build.Critical(ErrNegativeCurrency) 144 } else { 145 c.i.Mul(&x.i, y.Num()) 146 c.i.Div(&c.i, y.Denom()) 147 } 148 return 149 } 150 151 // MulTax returns a new Currency value c = x * 0.039, where 0.039 is a big.Rat. 152 func (x Currency) MulTax() (c Currency) { 153 c.i.Mul(&x.i, big.NewInt(39)) 154 c.i.Div(&c.i, big.NewInt(1000)) 155 return c 156 } 157 158 // RoundDown returns the largest multiple of y <= x. 159 func (x Currency) RoundDown(y Currency) (c Currency) { 160 diff := new(big.Int).Mod(&x.i, &y.i) 161 c.i.Sub(&x.i, diff) 162 return 163 } 164 165 // IsZero returns true if the value is 0, false otherwise. 166 func (x Currency) IsZero() bool { 167 return x.i.Sign() <= 0 168 } 169 170 // Sqrt returns a new Currency value y = sqrt(c). Result is rounded down to the 171 // nearest integer. 172 func (x Currency) Sqrt() (c Currency) { 173 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 // Sub returns a new Currency value c = x - y. Behavior is undefined when 180 // x < y. 181 func (x Currency) Sub(y Currency) (c Currency) { 182 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 // Uint64 converts a Currency to a uint64. An error is returned because this 192 // function is sometimes called on values that can be determined by users - 193 // rather than have all user-facing points do input checking, the input 194 // checking should happen at the base type. This minimizes the chances of a 195 // rogue user causing a build.Critical to be triggered. 196 func (x Currency) Uint64() (u uint64, err error) { 197 if x.Cmp(NewCurrency64(math.MaxUint64)) > 0 { 198 return 0, ErrUint64Overflow 199 } 200 return x.Big().Uint64(), nil 201 }