Add basic types Account and Msg

This commit is contained in:
Jae Kwon 2017-11-03 16:43:04 -05:00
parent df325ceaef
commit 7750a698f9
5 changed files with 330 additions and 0 deletions

21
types/account.go Normal file
View File

@ -0,0 +1,21 @@
package main
import "fmt"
func main() {
fmt.Println("vim-go")
}
type Account interface {
Get(key interface{}) (value interface{})
Address() []byte
PubKey() crypto.PubKey
// Serialize the Account to bytes.
Bytes() []byte
}
type AccountStore interface {
GetAccount(addr []byte) Account
SetAccount(acc Account)
}

197
types/coins.go Normal file
View File

@ -0,0 +1,197 @@
package types
import (
"fmt"
"regexp"
"strconv"
"strings"
)
type Coin struct {
Denom string `json:"denom"`
Amount int64 `json:"amount"`
}
func (coin Coin) String() string {
return fmt.Sprintf("%v%v", coin.Amount, coin.Denom)
}
//regex codes for extracting coins from string
var reDenom = regexp.MustCompile("([^\\d\\W]+)")
var reAmt = regexp.MustCompile("(\\d+)")
func ParseCoin(str string) (Coin, error) {
var coin Coin
if len(str) > 0 {
amt, err := strconv.Atoi(reAmt.FindString(str))
if err != nil {
return coin, err
}
denom := reDenom.FindString(str)
coin = Coin{denom, int64(amt)}
}
return coin, nil
}
//----------------------------------------
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]
}
func ParseCoins(str string) (Coins, error) {
split := strings.Split(str, ",")
var coins []Coin
for _, el := range split {
if len(el) > 0 {
coin, err := ParseCoin(el)
if err != nil {
return coins, err
}
coins = append(coins, coin)
}
}
return coins, nil
}
// Must be sorted, and not 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
}
}
// TODO: handle empty coins!
// Currently appends an empty coin ...
func (coinsA Coins) Plus(coinsB Coins) Coins {
sum := []Coin{}
indexA, indexB := 0, 0
lenA, lenB := len(coinsA), len(coinsB)
for {
if indexA == lenA {
if indexB == lenB {
return sum
} else {
return append(sum, coinsB[indexB:]...)
}
} else if indexB == lenB {
return append(sum, coinsA[indexA:]...)
}
coinA, coinB := coinsA[indexA], coinsB[indexB]
switch strings.Compare(coinA.Denom, coinB.Denom) {
case -1:
sum = append(sum, coinA)
indexA += 1
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 += 1
indexB += 1
case 1:
sum = append(sum, coinB)
indexB += 1
}
}
return sum
}
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
}
func (coinsA Coins) Minus(coinsB Coins) Coins {
return coinsA.Plus(coinsB.Negative())
}
func (coinsA Coins) IsGTE(coinsB Coins) bool {
diff := coinsA.Minus(coinsB)
if len(diff) == 0 {
return true
}
return diff.IsNonnegative()
}
func (coins Coins) IsZero() bool {
return len(coins) == 0
}
func (coinsA Coins) IsEqual(coinsB Coins) bool {
if len(coinsA) != len(coinsB) {
return false
}
for i := 0; i < len(coinsA); i++ {
if coinsA[i] != coinsB[i] {
return false
}
}
return true
}
func (coins Coins) IsPositive() bool {
if len(coins) == 0 {
return false
}
for _, coinAmount := range coins {
if coinAmount.Amount <= 0 {
return false
}
}
return true
}
func (coins Coins) IsNonnegative() bool {
if len(coins) == 0 {
return true
}
for _, coinAmount := range coins {
if coinAmount.Amount < 0 {
return false
}
}
return true
}

85
types/coins_test.go Normal file
View File

@ -0,0 +1,85 @@
package types
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
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, require := assert.New(t), require.New(t)
makeCoin := func(str string) Coin {
coin, err := ParseCoin(str)
require.Nil(err)
return coin
}
makeCoins := func(str string) Coins {
coin, err := ParseCoins(str)
require.Nil(err)
return coin
}
//testing ParseCoin Function
assert.Equal(Coin{}, makeCoin(""), "ParseCoin makes bad empty coin")
assert.Equal(Coin{"fooCoin", 1}, makeCoin("1fooCoin"), "ParseCoin makes bad coins")
assert.Equal(Coin{"barCoin", 10}, makeCoin("10 barCoin"), "ParseCoin makes bad coins")
//testing ParseCoins Function
assert.True(Coins{{"fooCoin", 1}}.IsEqual(makeCoins("1fooCoin")),
"ParseCoins doesn't parse a single coin")
assert.True(Coins{{"barCoin", 99}, {"fooCoin", 1}}.IsEqual(makeCoins("99barCoin,1fooCoin")),
"ParseCoins doesn't properly parse two coins")
assert.True(Coins{{"barCoin", 99}, {"fooCoin", 1}}.IsEqual(makeCoins("99 barCoin, 1 fooCoin")),
"ParseCoins doesn't properly parse two coins which use spaces")
}

27
types/msg.go Normal file
View File

@ -0,0 +1,27 @@
package types
import "github.com/tendermint/tmlibs/crypto"
// The parsed tx bytes is called a Msg.
type Msg interface {
Get(key interface{}) (value interface{})
Origin() (tx []byte)
// Signers() returns the crypto.PubKey of signers
// responsible for signing the Msg.
// CONTRACT: All signatures must be present to be valid.
// CONTRACT: Returns pubkeys in some deterministic order
// CONTRACT: Get(MsgKeySigners) compatible.
Signers() []crypto.PubKey
// Signatures() returns the crypto.Signature of sigenrs
// who signed the Msg.
// CONTRACT: Length returned is same as length of
// pubkeys returned from MsgKeySigners, and the order
// matches.
// CONTRACT: If the signature is missing (ie the Msg is
// invalid), then the corresponding signature is
// .Empty().
// CONTRACT: Get(MsgKeySignatures) compatible.
Signatures() []crypto.Signature
}