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  }