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  }