move coins and accounts out of types
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "github.com/tendermint/go-wire/gen"
|
||||
_ "github.com/clipperhouse/stringer"
|
||||
)
|
||||
@@ -1,29 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
type Address = crypto.Address
|
||||
|
||||
type Account interface {
|
||||
Address() Address
|
||||
|
||||
PubKey() crypto.PubKey
|
||||
SetPubKey(crypto.PubKey) error
|
||||
|
||||
GetCoins() Coins
|
||||
SetCoins(Coins) error
|
||||
|
||||
GetSequence() int64
|
||||
SetSequence(int64) error
|
||||
|
||||
Get(key interface{}) (value interface{}, err error)
|
||||
Set(key interface{}, value interface{}) error
|
||||
}
|
||||
|
||||
type AccountStore interface {
|
||||
NewAccountWithAddress(addr Address) Account
|
||||
GetAccount(addr Address) Account
|
||||
SetAccount(acc Account)
|
||||
}
|
||||
-271
@@ -1,271 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Coin hold some amount of one currency
|
||||
type Coin struct {
|
||||
Denom string `json:"denom"`
|
||||
Amount int64 `json:"amount"`
|
||||
}
|
||||
|
||||
// String provides a human-readable representation of a coin
|
||||
func (coin Coin) String() string {
|
||||
return fmt.Sprintf("%v%v", coin.Amount, coin.Denom)
|
||||
}
|
||||
|
||||
// IsZero returns if this represents no money
|
||||
func (coin Coin) IsZero() bool {
|
||||
return coin.Amount == 0
|
||||
}
|
||||
|
||||
// IsGTE returns true if they are the same type and the receiver is
|
||||
// an equal or greater value
|
||||
func (coin Coin) IsGTE(other Coin) bool {
|
||||
return (coin.Denom == other.Denom) &&
|
||||
(coin.Amount >= other.Amount)
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Coins
|
||||
|
||||
// Coins is a set of Coin, one per currency
|
||||
type Coins []Coin
|
||||
|
||||
func (coins Coins) String() string {
|
||||
if len(coins) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
out := ""
|
||||
for _, coin := range coins {
|
||||
out += fmt.Sprintf("%v,", coin.String())
|
||||
}
|
||||
return out[:len(out)-1]
|
||||
}
|
||||
|
||||
// IsValid asserts the Coins are sorted, and don't have 0 amounts
|
||||
func (coins Coins) IsValid() bool {
|
||||
switch len(coins) {
|
||||
case 0:
|
||||
return true
|
||||
case 1:
|
||||
return coins[0].Amount != 0
|
||||
default:
|
||||
lowDenom := coins[0].Denom
|
||||
for _, coin := range coins[1:] {
|
||||
if coin.Denom <= lowDenom {
|
||||
return false
|
||||
}
|
||||
if coin.Amount == 0 {
|
||||
return false
|
||||
}
|
||||
// we compare each coin against the last denom
|
||||
lowDenom = coin.Denom
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Plus combines to sets of coins
|
||||
//
|
||||
// TODO: handle empty coins!
|
||||
// Currently appends an empty coin ...
|
||||
func (coins Coins) Plus(coinsB Coins) Coins {
|
||||
sum := []Coin{}
|
||||
indexA, indexB := 0, 0
|
||||
lenA, lenB := len(coins), len(coinsB)
|
||||
for {
|
||||
if indexA == lenA {
|
||||
if indexB == lenB {
|
||||
return sum
|
||||
}
|
||||
return append(sum, coinsB[indexB:]...)
|
||||
} else if indexB == lenB {
|
||||
return append(sum, coins[indexA:]...)
|
||||
}
|
||||
coinA, coinB := coins[indexA], coinsB[indexB]
|
||||
switch strings.Compare(coinA.Denom, coinB.Denom) {
|
||||
case -1:
|
||||
sum = append(sum, coinA)
|
||||
indexA++
|
||||
case 0:
|
||||
if coinA.Amount+coinB.Amount == 0 {
|
||||
// ignore 0 sum coin type
|
||||
} else {
|
||||
sum = append(sum, Coin{
|
||||
Denom: coinA.Denom,
|
||||
Amount: coinA.Amount + coinB.Amount,
|
||||
})
|
||||
}
|
||||
indexA++
|
||||
indexB++
|
||||
case 1:
|
||||
sum = append(sum, coinB)
|
||||
indexB++
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
// Negative returns a set of coins with all amount negative
|
||||
func (coins Coins) Negative() Coins {
|
||||
res := make([]Coin, 0, len(coins))
|
||||
for _, coin := range coins {
|
||||
res = append(res, Coin{
|
||||
Denom: coin.Denom,
|
||||
Amount: -coin.Amount,
|
||||
})
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Minus subtracts a set of coins from another (adds the inverse)
|
||||
func (coins Coins) Minus(coinsB Coins) Coins {
|
||||
return coins.Plus(coinsB.Negative())
|
||||
}
|
||||
|
||||
// IsGTE returns True iff coins is NonNegative(), and for every
|
||||
// currency in coinsB, the currency is present at an equal or greater
|
||||
// amount in coinsB
|
||||
func (coins Coins) IsGTE(coinsB Coins) bool {
|
||||
diff := coins.Minus(coinsB)
|
||||
if len(diff) == 0 {
|
||||
return true
|
||||
}
|
||||
return diff.IsNotNegative()
|
||||
}
|
||||
|
||||
// IsZero returns true if there are no coins
|
||||
func (coins Coins) IsZero() bool {
|
||||
return len(coins) == 0
|
||||
}
|
||||
|
||||
// IsEqual returns true if the two sets of Coins have the same value
|
||||
func (coins Coins) IsEqual(coinsB Coins) bool {
|
||||
if len(coins) != len(coinsB) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(coins); i++ {
|
||||
if coins[i] != coinsB[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsPositive returns true if there is at least one coin, and all
|
||||
// currencies have a positive value
|
||||
func (coins Coins) IsPositive() bool {
|
||||
if len(coins) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, coinAmount := range coins {
|
||||
if coinAmount.Amount <= 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsNotNegative returns true if there is no currency with a negative value
|
||||
// (even no coins is true here)
|
||||
func (coins Coins) IsNotNegative() bool {
|
||||
if len(coins) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, coinAmount := range coins {
|
||||
if coinAmount.Amount < 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Sort interface
|
||||
|
||||
//nolint
|
||||
func (coins Coins) Len() int { return len(coins) }
|
||||
func (coins Coins) Less(i, j int) bool { return coins[i].Denom < coins[j].Denom }
|
||||
func (coins Coins) Swap(i, j int) { coins[i], coins[j] = coins[j], coins[i] }
|
||||
|
||||
var _ sort.Interface = Coins{}
|
||||
|
||||
// Sort is a helper function to sort the set of coins inplace
|
||||
func (coins Coins) Sort() { sort.Sort(coins) }
|
||||
|
||||
//----------------------------------------
|
||||
// Misc
|
||||
|
||||
type Coinser interface {
|
||||
GetCoins() Coins
|
||||
SetCoins(Coins)
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Parsing
|
||||
|
||||
var (
|
||||
// Denominations can be 3 ~ 16 characters long.
|
||||
reDnm = `[[:alpha:]][[:alnum:]]{2,15}`
|
||||
reAmt = `[[:digit:]]+`
|
||||
reSpc = `[[:space:]]*`
|
||||
reCoin = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reAmt, reSpc, reDnm))
|
||||
)
|
||||
|
||||
// ParseCoin parses a cli input for one coin type, returning errors if invalid.
|
||||
// This returns an error on an empty string as well.
|
||||
func ParseCoin(coinStr string) (coin Coin, err error) {
|
||||
coinStr = strings.TrimSpace(coinStr)
|
||||
|
||||
matches := reCoin.FindStringSubmatch(coinStr)
|
||||
if matches == nil {
|
||||
err = errors.Errorf("Invalid coin expression: %s", coinStr)
|
||||
return
|
||||
}
|
||||
denomStr, amountStr := matches[2], matches[1]
|
||||
|
||||
amount, err := strconv.Atoi(amountStr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return Coin{denomStr, int64(amount)}, nil
|
||||
}
|
||||
|
||||
// ParseCoins will parse out a list of coins separated by commas.
|
||||
// If nothing is provided, it returns nil Coins.
|
||||
// Returned coins are sorted.
|
||||
func ParseCoins(coinsStr string) (coins Coins, err error) {
|
||||
coinsStr = strings.TrimSpace(coinsStr)
|
||||
if len(coinsStr) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
coinStrs := strings.Split(coinsStr, ",")
|
||||
for _, coinStr := range coinStrs {
|
||||
coin, err := ParseCoin(coinStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
coins = append(coins, coin)
|
||||
}
|
||||
|
||||
// Sort coins for determinism.
|
||||
coins.Sort()
|
||||
|
||||
// Validate coins before returning.
|
||||
if !coins.IsValid() {
|
||||
return nil, errors.Errorf("ParseCoins invalid: %#v", coins)
|
||||
}
|
||||
|
||||
return coins, nil
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCoins(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
//Define the coins to be used in tests
|
||||
good := Coins{
|
||||
Coin{"GAS", 1},
|
||||
Coin{"MINERAL", 1},
|
||||
Coin{"TREE", 1},
|
||||
}
|
||||
neg := good.Negative()
|
||||
sum := good.Plus(neg)
|
||||
empty := Coins{
|
||||
Coin{"GOLD", 0},
|
||||
}
|
||||
badSort1 := Coins{
|
||||
Coin{"TREE", 1},
|
||||
Coin{"GAS", 1},
|
||||
Coin{"MINERAL", 1},
|
||||
}
|
||||
badSort2 := Coins{ // both are after the first one, but the second and third are in the wrong order
|
||||
Coin{"GAS", 1},
|
||||
Coin{"TREE", 1},
|
||||
Coin{"MINERAL", 1},
|
||||
}
|
||||
badAmt := Coins{
|
||||
Coin{"GAS", 1},
|
||||
Coin{"TREE", 0},
|
||||
Coin{"MINERAL", 1},
|
||||
}
|
||||
dup := Coins{
|
||||
Coin{"GAS", 1},
|
||||
Coin{"GAS", 1},
|
||||
Coin{"MINERAL", 1},
|
||||
}
|
||||
|
||||
assert.True(good.IsValid(), "Coins are valid")
|
||||
assert.True(good.IsPositive(), "Expected coins to be positive: %v", good)
|
||||
assert.True(good.IsGTE(empty), "Expected %v to be >= %v", good, empty)
|
||||
assert.False(neg.IsPositive(), "Expected neg coins to not be positive: %v", neg)
|
||||
assert.Zero(len(sum), "Expected 0 coins")
|
||||
assert.False(badSort1.IsValid(), "Coins are not sorted")
|
||||
assert.False(badSort2.IsValid(), "Coins are not sorted")
|
||||
assert.False(badAmt.IsValid(), "Coins cannot include 0 amounts")
|
||||
assert.False(dup.IsValid(), "Duplicate coin")
|
||||
|
||||
}
|
||||
|
||||
//Test the parse coin and parse coins functionality
|
||||
func TestParse(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cases := []struct {
|
||||
input string
|
||||
valid bool // if false, we expect an error on parse
|
||||
expected Coins // if valid is true, make sure this is returned
|
||||
}{
|
||||
{"", true, nil},
|
||||
{"1foo", true, Coins{{"foo", 1}}},
|
||||
{"10bar", true, Coins{{"bar", 10}}},
|
||||
{"99bar,1foo", true, Coins{{"bar", 99}, {"foo", 1}}},
|
||||
{"98 bar , 1 foo ", true, Coins{{"bar", 98}, {"foo", 1}}},
|
||||
{" 55\t \t bling\n", true, Coins{{"bling", 55}}},
|
||||
{"2foo, 97 bar", true, Coins{{"bar", 97}, {"foo", 2}}},
|
||||
{"5 mycoin,", false, nil}, // no empty coins in a list
|
||||
{"2 3foo, 97 bar", false, nil}, // 3foo is invalid coin name
|
||||
{"11me coin, 12you coin", false, nil}, // no spaces in coin names
|
||||
{"1.2btc", false, nil}, // amount must be integer
|
||||
{"5foo-bar", false, nil}, // once more, only letters in coin name
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
res, err := ParseCoins(tc.input)
|
||||
if !tc.valid {
|
||||
assert.NotNil(err, "%s: %#v", tc.input, res)
|
||||
} else if assert.Nil(err, "%s: %+v", tc.input, err) {
|
||||
assert.Equal(tc.expected, res)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSortCoins(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
good := Coins{
|
||||
Coin{"GAS", 1},
|
||||
Coin{"MINERAL", 1},
|
||||
Coin{"TREE", 1},
|
||||
}
|
||||
empty := Coins{
|
||||
Coin{"GOLD", 0},
|
||||
}
|
||||
badSort1 := Coins{
|
||||
Coin{"TREE", 1},
|
||||
Coin{"GAS", 1},
|
||||
Coin{"MINERAL", 1},
|
||||
}
|
||||
badSort2 := Coins{ // both are after the first one, but the second and third are in the wrong order
|
||||
Coin{"GAS", 1},
|
||||
Coin{"TREE", 1},
|
||||
Coin{"MINERAL", 1},
|
||||
}
|
||||
badAmt := Coins{
|
||||
Coin{"GAS", 1},
|
||||
Coin{"TREE", 0},
|
||||
Coin{"MINERAL", 1},
|
||||
}
|
||||
dup := Coins{
|
||||
Coin{"GAS", 1},
|
||||
Coin{"GAS", 1},
|
||||
Coin{"MINERAL", 1},
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
coins Coins
|
||||
before, after bool // valid before/after sort
|
||||
}{
|
||||
{good, true, true},
|
||||
{empty, false, false},
|
||||
{badSort1, false, true},
|
||||
{badSort2, false, true},
|
||||
{badAmt, false, false},
|
||||
{dup, false, false},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
assert.Equal(tc.before, tc.coins.IsValid())
|
||||
tc.coins.Sort()
|
||||
assert.Equal(tc.after, tc.coins.IsValid())
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
package types
|
||||
|
||||
import crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
type Msg interface {
|
||||
|
||||
// Get some property of the Msg.
|
||||
@@ -15,7 +17,7 @@ type Msg interface {
|
||||
// Signers returns the addrs of signers that must sign.
|
||||
// CONTRACT: All signatures must be present to be valid.
|
||||
// CONTRACT: Returns addrs in some deterministic order.
|
||||
Signers() []Address
|
||||
Signers() []crypto.Address
|
||||
}
|
||||
|
||||
type Tx interface {
|
||||
|
||||
Reference in New Issue
Block a user