Skip to content

Commit

Permalink
start working on #3614
Browse files Browse the repository at this point in the history
  • Loading branch information
Alessio Treglia committed Feb 15, 2019
1 parent 006acd2 commit e64baff
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 65 deletions.
87 changes: 73 additions & 14 deletions types/coin.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package types

import (
"errors"
"fmt"
"regexp"
"sort"
"strings"
)

var regexAnySpace = regexp.MustCompile(`\s`)

//-----------------------------------------------------------------------------
// Coin

Expand All @@ -24,28 +27,60 @@ type Coin struct {
Amount Int `json:"amount"`
}

// NewCoin returns a new coin with a denomination and amount. It will panic if
// the amount is negative.
// NewCoin returns a new coin with a denomination and amount.
// It will panic if the amount is negative.
func NewCoin(denom string, amount Int) Coin {
if amount.LT(ZeroInt()) {
panic(fmt.Sprintf("negative coin amount: %v\n", amount))
}
if strings.ToLower(denom) != denom {
panic(fmt.Sprintf("denom cannot contain upper case characters: %s\n", denom))
c := Coin{Denom: denom, Amount: amount}
if err := c.Validate(false); err != nil {
panic(err)
}

return Coin{
Denom: denom,
Amount: amount,
return c
}

// NewCoin returns a new coin with a denomination and amount.
// It will panic if the amount is less than or equal to zero.
func NewPositiveCoin(denom string, amount Int) Coin {
c := Coin{Denom: denom, Amount: amount}
if err := c.Validate(true); err != nil {
panic(err)
}

return c
}

// NewInt64Coin returns a new coin with a denomination and amount. It will panic
// if the amount is negative.
// NewInt64Coin returns a new coin with a denomination and amount.
// It will panic if the amount is negative.
func NewInt64Coin(denom string, amount int64) Coin {
return NewCoin(denom, NewInt(amount))
}

// NewInt64Coin returns a new coin with a denomination and amount.
// It will panic if the amount less than or equal to zero.
func NewPositiveInt64Coin(denom string, amount int64) Coin {
return NewPositiveCoin(denom, NewInt(amount))
}

// Validate validates coin's Amount and Denom. If failifzero is true, then
// it returns an error if Amount less than or equal to zero.
// If failifzero is false, then it returns an error if and only if Amount
// is less than zero.
func (c Coin) Validate(failifzero bool) error {
if err := validateIntCoinAmount(c.Amount, failifzero); err != nil {
panic(fmt.Errorf("%s: %s", err, c.Amount))
}

if err := validateCoinDenomContainsSpace(c.Denom); err != nil {
panic(fmt.Errorf("%s: %s", err, c.Denom))
}

if err := validateCoinDenomCase(c.Denom); err != nil {
panic(fmt.Errorf("%s: %s", err, c.Denom))
}

return nil
}

// String provides a human-readable representation of a coin
func (coin Coin) String() string {
return fmt.Sprintf("%v%v", coin.Amount, coin.Denom)
Expand Down Expand Up @@ -502,8 +537,8 @@ func ParseCoin(coinStr string) (coin Coin, err error) {
return Coin{}, fmt.Errorf("failed to parse coin amount: %s", amountStr)
}

if denomStr != strings.ToLower(denomStr) {
return Coin{}, fmt.Errorf("denom cannot contain upper case characters: %s", denomStr)
if err := validateCoinDenomCase(denomStr); err != nil {
return Coin{}, fmt.Errorf("%s: %s", err, denomStr)
}

return NewCoin(denomStr, amount), nil
Expand Down Expand Up @@ -537,3 +572,27 @@ func ParseCoins(coinsStr string) (coins Coins, err error) {

return coins, nil
}

func validateCoinDenomCase(denom string) error {
if denom != strings.ToLower(denom) {
return errors.New("denom cannot contain upper case characters")
}
return nil
}

func validateCoinDenomContainsSpace(denom string) error {
if regexAnySpace.MatchString(denom) {
return errors.New("denom cannot contain space characters")
}
return nil
}

func validateIntCoinAmount(amount Int, failifzero bool) error {
if failifzero && amount.LTE(ZeroInt()) {
return errors.New("non-positive coin amount")
}
if !failifzero && amount.LT(ZeroInt()) {
return errors.New("negative coin amount")
}
return nil
}
60 changes: 32 additions & 28 deletions types/coin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,34 @@ import (
// Coin tests

func TestCoin(t *testing.T) {
require.NotPanics(t, func() { NewInt64Coin("a", 0) })
require.Panics(t, func() { NewPositiveInt64Coin("a", 0) })
require.Panics(t, func() { NewInt64Coin("a", -1) })
require.Panics(t, func() { NewPositiveInt64Coin("a", -1) })

require.NotPanics(t, func() { NewCoin("a", NewInt(0)) })
require.Panics(t, func() { NewCoin("a", NewInt(-1)) })
require.Panics(t, func() { NewPositiveCoin("a", NewInt(0)) })
require.Panics(t, func() { NewPositiveCoin("a", NewInt(-1)) })

// test denom case
require.Panics(t, func() { NewInt64Coin("Atom", 10) })
require.Panics(t, func() { NewPositiveInt64Coin("Atom", 10) })
require.Panics(t, func() { NewCoin("Atom", NewInt(10)) })
require.Panics(t, func() { NewPositiveCoin("Atom", NewInt(10)) })

// test leading/trailing spaces

require.Panics(t, func() { NewInt64Coin("atom ", 10) })
require.Panics(t, func() { NewPositiveInt64Coin("atom ", 10) })
require.Panics(t, func() { NewCoin("atom ", NewInt(10)) })
require.Panics(t, func() { NewPositiveCoin("atom ", NewInt(10)) })

require.Panics(t, func() { NewInt64Coin("atom ", 10) })
require.Panics(t, func() { NewPositiveInt64Coin("atom ", 10) })
require.Panics(t, func() { NewCoin("atom ", NewInt(10)) })
require.Panics(t, func() { NewPositiveCoin("atom ", NewInt(10)) })

require.Equal(t, NewInt(5), NewInt64Coin("a", 5).Amount)
require.Equal(t, NewInt(5), NewCoin("a", NewInt(5)).Amount)
}
Expand Down Expand Up @@ -458,55 +482,35 @@ func TestSortCoins(t *testing.T) {
func TestAmountOf(t *testing.T) {
case0 := Coins{}
case1 := Coins{
NewInt64Coin("", 0),
}
case2 := Coins{
NewInt64Coin(" ", 0),
}
case3 := Coins{
NewInt64Coin("gold", 0),
}
case4 := Coins{
case2 := Coins{
NewInt64Coin("gas", 1),
NewInt64Coin("mineral", 1),
NewInt64Coin("tree", 1),
}
case5 := Coins{
case3 := Coins{
NewInt64Coin("mineral", 1),
NewInt64Coin("tree", 1),
}
case6 := Coins{
NewInt64Coin("", 6),
}
case7 := Coins{
NewInt64Coin(" ", 7),
}
case8 := Coins{
case4 := Coins{
NewInt64Coin("gas", 8),
}

cases := []struct {
coins Coins
amountOf int64
amountOfSpace int64
amountOfGAS int64
amountOfMINERAL int64
amountOfTREE int64
}{
{case0, 0, 0, 0, 0, 0},
{case1, 0, 0, 0, 0, 0},
{case2, 0, 0, 0, 0, 0},
{case3, 0, 0, 0, 0, 0},
{case4, 0, 0, 1, 1, 1},
{case5, 0, 0, 0, 1, 1},
{case6, 6, 0, 0, 0, 0},
{case7, 0, 7, 0, 0, 0},
{case8, 0, 0, 8, 0, 0},
{case0, 0, 0, 0},
{case1, 0, 0, 0},
{case2, 1, 1, 1},
{case3, 0, 1, 1},
{case4, 8, 0, 0},
}

for _, tc := range cases {
assert.Equal(t, NewInt(tc.amountOf), tc.coins.AmountOf(""))
assert.Equal(t, NewInt(tc.amountOfSpace), tc.coins.AmountOf(" "))
assert.Equal(t, NewInt(tc.amountOfGAS), tc.coins.AmountOf("gas"))
assert.Equal(t, NewInt(tc.amountOfMINERAL), tc.coins.AmountOf("mineral"))
assert.Equal(t, NewInt(tc.amountOfTREE), tc.coins.AmountOf("tree"))
Expand Down
94 changes: 71 additions & 23 deletions types/dec_coin.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,46 +18,84 @@ type DecCoin struct {
Amount Dec `json:"amount"`
}

// NewDecCoin returns a new coin with a denomination and amount.
// It will panic if the amount is negative.
func NewDecCoin(denom string, amount int64) DecCoin {
if amount < 0 {
panic(fmt.Sprintf("negative decimal coin amount: %v\n", amount))
}
if strings.ToLower(denom) != denom {
panic(fmt.Sprintf("denom cannot contain upper case characters: %s\n", denom))
c := DecCoin{Denom: denom, Amount: NewDec(amount)}
if err := c.Validate(false); err != nil {
panic(err)
}
return c
}

return DecCoin{
Denom: denom,
Amount: NewDec(amount),
// NewDecCoin returns a new coin with a denomination and amount.
// It will panic if the amount is less than or equal to zero.
func NewPositiveDecCoin(denom string, amount int64) DecCoin {
c := DecCoin{Denom: denom, Amount: NewDec(amount)}
if err := c.Validate(true); err != nil {
panic(err)
}
return c
}

// NewDecCoinFromDec returns a new coin with a denomination and
// decimal amount. It will panic if the amount is negative.
func NewDecCoinFromDec(denom string, amount Dec) DecCoin {
if amount.LT(ZeroDec()) {
panic(fmt.Sprintf("negative decimal coin amount: %v\n", amount))
}
if strings.ToLower(denom) != denom {
panic(fmt.Sprintf("denom cannot contain upper case characters: %s\n", denom))
c := DecCoin{Denom: denom, Amount: amount}
if err := c.Validate(false); err != nil {
panic(err)
}
return c
}

return DecCoin{
Denom: denom,
Amount: amount,
// NewDecCoinFromDec returns a new coin with a denomination and
// It will panic if the amount is less than or equal to zero.
func NewPositiveDecCoinFromDec(denom string, amount Dec) DecCoin {
c := DecCoin{Denom: denom, Amount: amount}
if err := c.Validate(true); err != nil {
panic(err)
}
return c
}

// NewDecCoinFromCoin returns a new DecCoin from a Coin.
// It will panic if the amount is negative.
func NewDecCoinFromCoin(coin Coin) DecCoin {
if coin.Amount.LT(ZeroInt()) {
panic(fmt.Sprintf("negative decimal coin amount: %v\n", coin.Amount))
c := DecCoin{Denom: coin.Denom, Amount: NewDecFromInt(coin.Amount)}
if err := c.Validate(false); err != nil {
panic(err)
}
return c
}

// NewDecCoinFromCoin returns a new DecCoin from a Coin.
// It will panic if the amount is negative.
func NewPositiveDecCoinFromCoin(coin Coin) DecCoin {
c := DecCoin{Denom: coin.Denom, Amount: NewDecFromInt(coin.Amount)}
if err := c.Validate(true); err != nil {
panic(err)
}
return c
}

// Validate validates coin's Amount and Denom. If failifzero is true, then
// it returns an error if Amount less than or equal to zero.
// If failifzero is false, then it returns an error if and only if Amount
// is less than zero.
func (c DecCoin) Validate(failifzero bool) error {
if err := validateDecCoinAmount(c.Amount, failifzero); err != nil {
panic(fmt.Errorf("%s: %s", err, c.Amount))
}
if strings.ToLower(coin.Denom) != coin.Denom {
panic(fmt.Sprintf("denom cannot contain upper case characters: %s\n", coin.Denom))

if err := validateCoinDenomContainsSpace(c.Denom); err != nil {
panic(fmt.Errorf("%s: %s", err, c.Denom))
}

return DecCoin{
Denom: coin.Denom,
Amount: NewDecFromInt(coin.Amount),
if err := validateCoinDenomCase(c.Denom); err != nil {
panic(fmt.Errorf("%s: %s", err, c.Denom))
}

return nil
}

// Adds amounts of two coins with same denom
Expand Down Expand Up @@ -397,3 +435,13 @@ func ParseDecCoins(coinsStr string) (coins DecCoins, err error) {

return coins, nil
}

func validateDecCoinAmount(amount Dec, failifzero bool) error {
if failifzero && amount.LTE(ZeroDec()) {
return errors.New("non-positive coin amount")
}
if !failifzero && amount.LT(ZeroDec()) {
return errors.New("negative coin amount")
}
return nil
}

0 comments on commit e64baff

Please sign in to comment.