This repository has been archived by the owner on Jul 20, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathcurrency.go
288 lines (252 loc) · 7.9 KB
/
currency.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
package types
// 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.
import (
"bytes"
"errors"
"fmt"
"io"
"math"
"math/big"
"strings"
"github.com/threefoldtech/rivine/pkg/encoding/rivbin"
"github.com/threefoldtech/rivine/build"
"github.com/threefoldtech/rivine/pkg/encoding/siabin"
)
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
}
)
var (
// ZeroCurrency defines a currency of value zero.
ZeroCurrency = NewCurrency64(0)
// ErrNegativeCurrency is the error that is returned if performing an
// operation results in a negative currency.
ErrNegativeCurrency = errors.New("negative currency not allowed")
// 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")
)
// NewCurrency creates a Currency value from a big.Int. Undefined behavior
// occurs if a negative input is used.
func NewCurrency(b *big.Int) (c Currency) {
if b.Sign() < 0 {
build.Critical(ErrNegativeCurrency)
} else {
c.i = *b
}
return
}
// NewCurrency64 creates a Currency value from a uint64.
func NewCurrency64(x uint64) (c Currency) {
c.i.SetUint64(x)
return
}
// Add returns a new Currency value c = x + y
func (x Currency) Add(y Currency) (c Currency) {
c.i.Add(&x.i, &y.i)
return
}
// Big returns the value of c as a *big.Int. Importantly, it does not provide
// access to the c's internal big.Int object, only a copy.
func (x Currency) Big() *big.Int {
return new(big.Int).Set(&x.i)
}
// Cmp compares two Currency values. The return value follows the convention
// of math/big.
func (x Currency) Cmp(y Currency) int {
return x.i.Cmp(&y.i)
}
// 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))
}
// Div returns a new Currency value c = x / y.
func (x Currency) Div(y Currency) (c Currency) {
c.i.Div(&x.i, &y.i)
return
}
// 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
}
// Equals returns true if x and y have the same value.
func (x Currency) Equals(y Currency) bool {
return x.Cmp(y) == 0
}
// Equals64 returns true if x and y have the same value.
func (x Currency) Equals64(y uint64) bool {
return x.Cmp64(y) == 0
}
// Mul returns a new Currency value c = x * y.
func (x Currency) Mul(y Currency) (c Currency) {
c.i.Mul(&x.i, &y.i)
return
}
// 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
}
// COMPATv0.4.0 - until the first 10e3 blocks have been archived, MulFloat is
// needed while verifying the first set of blocks.
//
// 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 {
build.Critical(ErrNegativeCurrency)
} 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.
func (x Currency) MulRat(y *big.Rat) (c Currency) {
if y.Sign() < 0 {
build.Critical(ErrNegativeCurrency)
} else {
c.i.Mul(&x.i, y.Num())
c.i.Div(&c.i, y.Denom())
}
return
}
// RoundDown returns the largest multiple of y <= x.
func (x Currency) RoundDown(y Currency) (c Currency) {
diff := new(big.Int).Mod(&x.i, &y.i)
c.i.Sub(&x.i, diff)
return
}
// IsZero returns true if the value is 0, false otherwise.
func (c Currency) IsZero() bool {
return c.i.Sign() <= 0
}
// Sqrt returns a new Currency value y = sqrt(c). Result is rounded down to the
// nearest integer.
func (x Currency) Sqrt() (c Currency) {
f, _ := new(big.Rat).SetInt(&x.i).Float64()
sqrt := new(big.Rat).SetFloat64(math.Sqrt(f))
c.i.Div(sqrt.Num(), sqrt.Denom())
return
}
// 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 {
c = x
build.Critical(ErrNegativeCurrency)
} else {
c.i.Sub(&x.i, &y.i)
}
return
}
// 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.
func (c Currency) Uint64() (u uint64, err error) {
if c.Cmp(NewCurrency64(math.MaxUint64)) > 0 {
return 0, ErrUint64Overflow
}
return c.Big().Uint64(), nil
}
// MarshalJSON implements the json.Marshaler interface.
func (c Currency) MarshalJSON() ([]byte, error) {
// Must enclosed the value in quotes; otherwise JS will convert it to a
// double and lose precision.
return []byte(`"` + c.String() + `"`), nil
}
// UnmarshalJSON implements the json.Unmarshaler interface. An error is
// returned if a negative number is provided.
func (c *Currency) UnmarshalJSON(b []byte) error {
// UnmarshalJSON does not expect quotes
b = bytes.Trim(b, `"`)
err := c.i.UnmarshalJSON(b)
if err != nil {
return err
}
if c.i.Sign() < 0 {
c.i = *big.NewInt(0)
return ErrNegativeCurrency
}
return nil
}
// MarshalSia implements the siabin.SiaMarshaler interface. It writes the
// byte-slice representation of the Currency's internal big.Int to w. Note
// that as the bytes of the big.Int correspond to the absolute value of the
// integer, there is no way to marshal a negative Currency.
func (c Currency) MarshalSia(w io.Writer) error {
return siabin.WritePrefix(w, c.i.Bytes())
}
// UnmarshalSia implements the siabin.SiaUnmarshaler interface.
func (c *Currency) UnmarshalSia(r io.Reader) error {
b, err := siabin.ReadPrefix(r, 256)
if err != nil {
return err
}
var dec Currency
dec.i.SetBytes(b)
*c = dec
return nil
}
// MarshalRivine implements the rivbin.MarshalRivine interface. It writes the
// byte-slice representation of the Currency's internal big.Int to w. Note
// that as the bytes of the big.Int correspond to the absolute value of the
// integer, there is no way to marshal a negative Currency.
func (c Currency) MarshalRivine(w io.Writer) error {
return rivbin.WriteDataSlice(w, c.i.Bytes())
}
// UnmarshalRivine implements the rivbin.RivineMarshaler interface.
func (c *Currency) UnmarshalRivine(r io.Reader) error {
b, err := rivbin.ReadDataSlice(r, 256)
if err != nil {
return err
}
var dec Currency
dec.i.SetBytes(b)
*c = dec
return nil
}
// String implements the fmt.Stringer interface.
func (c Currency) String() string {
return c.i.String()
}
// LoadString loads the given Currency from a string.
func (c *Currency) LoadString(str string) error {
str = strings.TrimLeft(str, "0")
if str == "" {
str = "0"
}
return c.i.UnmarshalText([]byte(str))
}
// Scan implements the fmt.Scanner interface, allowing Currency values to be
// scanned from text.
func (c *Currency) Scan(s fmt.ScanState, ch rune) error {
var dec Currency
err := dec.i.Scan(s, ch)
if err != nil {
return err
}
if dec.i.Sign() < 0 {
return ErrNegativeCurrency
}
*c = dec
return nil
}