Commit 09c3e722 authored by David Vorick's avatar David Vorick

add testing for consensus type

parent 7d7eb852
......@@ -9,11 +9,16 @@ import (
var (
ZeroCurrency = NewCurrency64(0)
ErrNegativeCurrency = errors.New("negative currency not allowed")
)
// currency.go defines the internal currency object. One major design goal of
// the currency type is immutability. Another is non-negativity: the currency
// object should never have a negative value.
// 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^600), however exceeding
// this value will not result in overflow.
// A Currency represents a number of siacoins or siafunds. Internally, a
// Currency value is unbounded; however, Currency values sent over the wire
......@@ -24,9 +29,16 @@ type Currency struct {
i big.Int
}
// NewCurrency creates a Currency value from a big.Int.
// 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) {
c.i = *b
if c.Cmp(ZeroCurrency) < 0 {
if DEBUG {
panic("cannot have a negative currency")
}
c = Currency{}
}
return
}
......@@ -67,7 +79,15 @@ func (c Currency) Mul(x Currency) (y Currency) {
}
// MulFloat returns a new Currency value y = c * x, where x is a float64.
// Behavior is undefined when x is negative.
func (c Currency) MulFloat(x float64) (y Currency) {
if x < 0 {
if DEBUG {
panic("cannot multiple currency by a negative number")
}
return
}
yRat := new(big.Rat).Mul(
new(big.Rat).SetInt(&c.i),
new(big.Rat).SetFloat64(x),
......@@ -83,7 +103,8 @@ func (c Currency) RoundDown(n uint64) (y Currency) {
return
}
// Sqrt returns a new Currency value y = sqrt(c)
// Sqrt returns a new Currency value y = sqrt(c). Result is rounded down to the
// nearest integer.
func (c Currency) Sqrt() (y Currency) {
f, _ := new(big.Rat).SetInt(&c.i).Float64()
sqrt := new(big.Rat).SetFloat64(math.Sqrt(f))
......@@ -92,9 +113,16 @@ func (c Currency) Sqrt() (y Currency) {
}
// Sub returns a new Currency value y = c - x.
func (c Currency) Sub(x Currency) (y Currency) {
func (c Currency) Sub(x Currency) Currency {
var y Currency
y.i.Sub(&c.i, &x.i)
return
if y.Cmp(ZeroCurrency) < 0 {
if DEBUG {
panic("subtraction resulted in negative currency")
}
return x
}
return y
}
// MarshalJSON implements the json.Marshaler interface.
......@@ -102,14 +130,16 @@ func (c Currency) MarshalJSON() ([]byte, error) {
return c.i.MarshalJSON()
}
// UnmarshalJSON implements the json.Unmarshaler interface.
// UnmarshalJSON implements the json.Unmarshaler interface. An error is
// returned if a negative number is provided.
func (c *Currency) UnmarshalJSON(b []byte) error {
err := c.i.UnmarshalJSON(b)
if err != nil {
return err
}
if c.Cmp(ZeroCurrency) < 0 {
return errors.New("cannot have a negative currency")
c.i = *big.NewInt(0)
return ErrNegativeCurrency
}
return nil
}
......@@ -119,11 +149,11 @@ func (c *Currency) UnmarshalJSON(b []byte) error {
// with a single byte indicating the length of the slice.
func (c Currency) MarshalSia() []byte {
b := c.i.Bytes()
if DEBUG {
if len(b) > 255 {
panic(len(b))
if len(b) > 255 {
if DEBUG {
panic("attempting to marshal a too-big currency type")
}
return nil
}
return append(
......
package consensus
import (
"bytes"
"math/big"
"testing"
)
// TestCurrencyToBig tests the Big method for the currency type
func TestCurrencyToBig(t *testing.T) {
c := NewCurrency64(125)
cb := c.Big()
b := big.NewInt(125)
if b.Cmp(cb) != 0 {
t.Error("currency to big has failed")
}
}
// TestCurrencySqrt checks that the sqrt function of the currency type has been
// correctly implemented.
func TestCurrencySqrt(t *testing.T) {
c8 := NewCurrency64(8)
c64 := NewCurrency64(64)
c80 := NewCurrency64(80)
sqrt64 := c64.Sqrt()
sqrt80 := c80.Sqrt()
if c8.Cmp(sqrt64) != 0 {
t.Error("square root of 64 should be 8")
}
if c8.Cmp(sqrt80) != 0 {
t.Error("square root of 80 should be 8")
}
}
// TestMarshalJSON probes the MarshalJSON and UnmarshalJSON functions.
func TestMarshalJSON(t *testing.T) {
b30 := big.NewInt(30)
c30 := NewCurrency64(30)
bMar30, err := b30.MarshalJSON()
if err != nil {
t.Fatal(err)
}
cMar30, err := c30.MarshalJSON()
if err != nil {
t.Fatal(err)
}
if bytes.Compare(bMar30, cMar30) != 0 {
t.Error("Currency does not match the marshalling of its math/big equivalent")
}
var cUmar30 Currency
err = cUmar30.UnmarshalJSON(cMar30)
if err != nil {
t.Fatal(err)
}
if c30.Cmp(cUmar30) != 0 {
t.Error("Incorrect unmarshalling of currency type.")
}
}
// TestMarshalSia probes the MarshalSia and UnmarshalSia functions.
func TestMarshalSia(t *testing.T) {
c := NewCurrency64(1656)
cMar := c.MarshalSia()
var cUmar Currency
n := cUmar.UnmarshalSia(cMar)
if c.Cmp(cUmar) != 0 {
t.Error("marshal and unmarshal mismatch for currency type")
}
if n != len(cMar) {
t.Fatal("unmarshal sia is returning the wrong length")
}
}
// TestMarshalSiaOverflow checks behavior when marshalling a value too large to
// fit inside of the currency type's encoding scheme.
func TestMarshalSiaOverflow(t *testing.T) {
// In debug mode, attempting to get a negative currency results in a panic.
defer func() {
r := recover()
if r == nil {
t.Error("no panic occured when trying to create a negative currency")
}
}()
// Get a currency that's going to be more than 255 bytes. This is achieved
// by squaring a 200 byte currency.
large := make([]byte, 201)
large[0] = 200 // provide a prefix indicating the length of the remaining slice.
large[1] = 1 // set the first value to '1' so the number isn't just 0.
var largeC Currency
_ = largeC.UnmarshalSia(large)
largeC = largeC.Mul(largeC)
// Try to marshal the large currency.
_ = largeC.MarshalSia()
}
// TestNegativeCurrencyMulFloat checks that negative numbers are rejected when
// calling MulFloat on the currency type.
func TestNegativeCurrencyMulFloat(t *testing.T) {
// In debug mode, attempting to get a negative currency results in a panic.
defer func() {
r := recover()
if r == nil {
t.Error("no panic occured when trying to create a negative currency")
}
}()
c := NewCurrency64(12)
_ = c.MulFloat(-1)
}
// TestNegativeCurrencySub checks that negative numbers are prevented when
// using subtraction on the currency type.
func TestNegativeCurrencySub(t *testing.T) {
// In debug mode, attempting to get a negative currency results in a panic.
defer func() {
r := recover()
if r == nil {
t.Error("no panic occured when trying to create a negative currency")
}
}()
c1 := NewCurrency64(1)
c2 := NewCurrency64(2)
_ = c1.Sub(c2)
}
// TestNegativeCurrencyUnmarshalJSON tries to unmarshal a negative number from
// JSON.
func TestNegativeCurrencyUnmarshalJSON(t *testing.T) {
// Marshal a 2 digit number.
c := NewCurrency64(35)
cMar, err := c.MarshalJSON()
if err != nil {
t.Fatal(err)
}
// Change the first digit to a negative character.
cMar[0] = 45
// Try unmarshalling the negative currency.
var cNeg Currency
err = cNeg.UnmarshalJSON(cMar)
if err != ErrNegativeCurrency {
t.Error("expecting ErrNegativeCurrency:", err)
}
if cNeg.Cmp(ZeroCurrency) < 0 {
t.Error("negative currency returned")
}
}
// TestNegativeCurrencies tries an array of ways to produce a negative currency.
func TestNegativeNewCurrency(t *testing.T) {
// In debug mode, attempting to get a negative currency results in a panic.
defer func() {
r := recover()
if r == nil {
t.Error("no panic occured when trying to create a negative currency")
}
}()
// Try to create a new currency from a negative number.
negBig := big.NewInt(-1)
_ = NewCurrency(negBig)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment