github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/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  	"bytes"
    12  	"errors"
    13  	"fmt"
    14  	"io"
    15  	"math"
    16  	"math/big"
    17  
    18  	"github.com/NebulousLabs/Sia/build"
    19  	"github.com/NebulousLabs/Sia/encoding"
    20  )
    21  
    22  type (
    23  	// A Currency represents a number of siacoins or siafunds. Internally, a
    24  	// Currency value is unbounded; however, Currency values sent over the wire
    25  	// protocol are subject to a maximum size of 255 bytes (approximately 10^614).
    26  	// Unlike the math/big library, whose methods modify their receiver, all
    27  	// arithmetic Currency methods return a new value. Currency cannot be negative.
    28  	Currency struct {
    29  		i big.Int
    30  	}
    31  )
    32  
    33  var (
    34  	// ZeroCurrency defines a currency of value zero.
    35  	ZeroCurrency = NewCurrency64(0)
    36  
    37  	// ErrNegativeCurrency is the error that is returned if performing an
    38  	// operation results in a negative currency.
    39  	ErrNegativeCurrency = errors.New("negative currency not allowed")
    40  
    41  	// ErrUint64Overflow is the error that is returned if converting to a
    42  	// unit64 would cause an overflow.
    43  	ErrUint64Overflow = errors.New("cannot return the uint64 of this currency - result is an overflow")
    44  )
    45  
    46  // NewCurrency creates a Currency value from a big.Int. Undefined behavior
    47  // occurs if a negative input is used.
    48  func NewCurrency(b *big.Int) (c Currency) {
    49  	if b.Sign() < 0 {
    50  		build.Critical(ErrNegativeCurrency)
    51  	} else {
    52  		c.i = *b
    53  	}
    54  	return
    55  }
    56  
    57  // NewCurrency64 creates a Currency value from a uint64.
    58  func NewCurrency64(x uint64) (c Currency) {
    59  	c.i.SetUint64(x)
    60  	return
    61  }
    62  
    63  // Add returns a new Currency value c = x + y
    64  func (x Currency) Add(y Currency) (c Currency) {
    65  	c.i.Add(&x.i, &y.i)
    66  	return
    67  }
    68  
    69  // Big returns the value of c as a *big.Int. Importantly, it does not provide
    70  // access to the c's internal big.Int object, only a copy.
    71  func (c Currency) Big() *big.Int {
    72  	return new(big.Int).Set(&c.i)
    73  }
    74  
    75  // Cmp compares two Currency values. The return value follows the convention
    76  // of math/big.
    77  func (x Currency) Cmp(y Currency) int {
    78  	return x.i.Cmp(&y.i)
    79  }
    80  
    81  // Div returns a new Currency value c = x / y.
    82  func (x Currency) Div(y Currency) (c Currency) {
    83  	c.i.Div(&x.i, &y.i)
    84  	return
    85  }
    86  
    87  // Div64 returns a new Currency value c = x / y.
    88  func (x Currency) Div64(y uint64) (c Currency) {
    89  	c.i.Div(&x.i, new(big.Int).SetUint64(y))
    90  	return
    91  }
    92  
    93  // Mul returns a new Currency value c = x * y.
    94  func (x Currency) Mul(y Currency) (c Currency) {
    95  	c.i.Mul(&x.i, &y.i)
    96  	return
    97  }
    98  
    99  // Mul64 returns a new Currency value c = x * y.
   100  func (x Currency) Mul64(y uint64) (c Currency) {
   101  	c.i.Mul(&x.i, new(big.Int).SetUint64(y))
   102  	return
   103  }
   104  
   105  // COMPATv0.4.0 - until the first 10e3 blocks have been archived, MulFloat is
   106  // needed while verifying the first set of blocks.
   107  //
   108  // MulFloat returns a new Currency value y = c * x, where x is a float64.
   109  // Behavior is undefined when x is negative.
   110  func (x Currency) MulFloat(y float64) (c Currency) {
   111  	if y < 0 {
   112  		build.Critical(ErrNegativeCurrency)
   113  	} else {
   114  		cRat := new(big.Rat).Mul(
   115  			new(big.Rat).SetInt(&x.i),
   116  			new(big.Rat).SetFloat64(y),
   117  		)
   118  		c.i.Div(cRat.Num(), cRat.Denom())
   119  	}
   120  	return
   121  }
   122  
   123  // MulRat returns a new Currency value c = x * y, where y is a big.Rat.
   124  func (x Currency) MulRat(y *big.Rat) (c Currency) {
   125  	if y.Sign() < 0 {
   126  		build.Critical(ErrNegativeCurrency)
   127  	} else {
   128  		c.i.Mul(&x.i, y.Num())
   129  		c.i.Div(&c.i, y.Denom())
   130  	}
   131  	return
   132  }
   133  
   134  // MulTax returns a new Currency value c = x * 0.039, where 0.039 is a big.Rat.
   135  func (x Currency) MulTax() (c Currency) {
   136  	c.i.Mul(&x.i, big.NewInt(39))
   137  	c.i.Div(&c.i, big.NewInt(1000))
   138  	return c
   139  }
   140  
   141  // RoundDown returns the largest multiple of y <= x.
   142  func (x Currency) RoundDown(y Currency) (c Currency) {
   143  	diff := new(big.Int).Mod(&x.i, &y.i)
   144  	c.i.Sub(&x.i, diff)
   145  	return
   146  }
   147  
   148  // IsZero returns true if the value is 0, false otherwise.
   149  func (c Currency) IsZero() bool {
   150  	return c.i.Sign() <= 0
   151  }
   152  
   153  // Sqrt returns a new Currency value y = sqrt(c). Result is rounded down to the
   154  // nearest integer.
   155  func (x Currency) Sqrt() (c Currency) {
   156  	f, _ := new(big.Rat).SetInt(&x.i).Float64()
   157  	sqrt := new(big.Rat).SetFloat64(math.Sqrt(f))
   158  	c.i.Div(sqrt.Num(), sqrt.Denom())
   159  	return
   160  }
   161  
   162  // Sub returns a new Currency value c = x - y. Behavior is undefined when
   163  // x < y.
   164  func (x Currency) Sub(y Currency) (c Currency) {
   165  	if x.Cmp(y) < 0 {
   166  		c = x
   167  		build.Critical(ErrNegativeCurrency)
   168  	} else {
   169  		c.i.Sub(&x.i, &y.i)
   170  	}
   171  	return
   172  }
   173  
   174  // Uint64 converts a Currency to a uint64. An error is returned because this
   175  // function is sometimes called on values that can be determined by users -
   176  // rather than have all user-facing points do input checking, the input
   177  // checking should happen at the base type. This minimizes the chances of a
   178  // rogue user causing a build.Critical to be triggered.
   179  func (c Currency) Uint64() (u uint64, err error) {
   180  	if c.Cmp(NewCurrency64(math.MaxUint64)) > 0 {
   181  		return 0, ErrUint64Overflow
   182  	}
   183  	return c.Big().Uint64(), nil
   184  }
   185  
   186  // MarshalJSON implements the json.Marshaler interface.
   187  func (c Currency) MarshalJSON() ([]byte, error) {
   188  	// Must enclosed the value in quotes; otherwise JS will convert it to a
   189  	// double and lose precision.
   190  	return []byte(`"` + c.String() + `"`), nil
   191  }
   192  
   193  // UnmarshalJSON implements the json.Unmarshaler interface. An error is
   194  // returned if a negative number is provided.
   195  func (c *Currency) UnmarshalJSON(b []byte) error {
   196  	// UnmarshalJSON does not expect quotes
   197  	b = bytes.Trim(b, `"`)
   198  	err := c.i.UnmarshalJSON(b)
   199  	if err != nil {
   200  		return err
   201  	}
   202  	if c.i.Sign() < 0 {
   203  		c.i = *big.NewInt(0)
   204  		return ErrNegativeCurrency
   205  	}
   206  	return nil
   207  }
   208  
   209  // MarshalSia implements the encoding.SiaMarshaler interface. It writes the
   210  // byte-slice representation of the Currency's internal big.Int to w. Note
   211  // that as the bytes of the big.Int correspond to the absolute value of the
   212  // integer, there is no way to marshal a negative Currency.
   213  func (c Currency) MarshalSia(w io.Writer) error {
   214  	return encoding.WritePrefix(w, c.i.Bytes())
   215  }
   216  
   217  // UnmarshalSia implements the encoding.SiaUnmarshaler interface.
   218  func (c *Currency) UnmarshalSia(r io.Reader) error {
   219  	b, err := encoding.ReadPrefix(r, 256)
   220  	if err != nil {
   221  		return err
   222  	}
   223  	c.i.SetBytes(b)
   224  	return nil
   225  }
   226  
   227  // String implements the fmt.Stringer interface.
   228  func (c Currency) String() string {
   229  	return c.i.String()
   230  }
   231  
   232  // Scan implements the fmt.Scanner interface, allowing Currency values to be
   233  // scanned from text.
   234  func (c *Currency) Scan(s fmt.ScanState, ch rune) error {
   235  	err := c.i.Scan(s, ch)
   236  	if err != nil {
   237  		return err
   238  	}
   239  	if c.i.Sign() < 0 {
   240  		return ErrNegativeCurrency
   241  	}
   242  	return nil
   243  }