cosmos-sdk/types/coin_test.go

1047 lines
35 KiB
Go

package types_test
import (
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/suite"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
)
var (
testDenom1 = "atom"
testDenom2 = "muon"
)
type coinTestSuite struct {
suite.Suite
ca0, ca1, ca2, cm0, cm1, cm2 sdk.Coin
}
func TestCoinTestSuite(t *testing.T) {
suite.Run(t, new(coinTestSuite))
}
func (s *coinTestSuite) SetupSuite() {
s.T().Parallel()
zero := sdk.NewInt(0)
one := sdk.OneInt()
two := sdk.NewInt(2)
s.ca0, s.ca1, s.ca2 = sdk.Coin{testDenom1, zero}, sdk.Coin{testDenom1, one}, sdk.Coin{testDenom1, two}
s.cm0, s.cm1, s.cm2 = sdk.Coin{testDenom2, zero}, sdk.Coin{testDenom2, one}, sdk.Coin{testDenom2, two}
}
// ----------------------------------------------------------------------------
// Coin tests
func (s *coinTestSuite) TestCoin() {
s.Require().Panics(func() { sdk.NewInt64Coin(testDenom1, -1) })
s.Require().Panics(func() { sdk.NewCoin(testDenom1, sdk.NewInt(-1)) })
s.Require().Equal(sdk.NewInt(10), sdk.NewInt64Coin(strings.ToUpper(testDenom1), 10).Amount)
s.Require().Equal(sdk.NewInt(10), sdk.NewCoin(strings.ToUpper(testDenom1), sdk.NewInt(10)).Amount)
s.Require().Equal(sdk.NewInt(5), sdk.NewInt64Coin(testDenom1, 5).Amount)
s.Require().Equal(sdk.NewInt(5), sdk.NewCoin(testDenom1, sdk.NewInt(5)).Amount)
}
func (s *coinTestSuite) TestCoin_String() {
coin := sdk.NewCoin(testDenom1, sdk.NewInt(10))
s.Require().Equal(fmt.Sprintf("10%s", testDenom1), coin.String())
}
func (s *coinTestSuite) TestIsEqualCoin() {
cases := []struct {
inputOne sdk.Coin
inputTwo sdk.Coin
expected bool
panics bool
}{
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), true, false},
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), false, true},
{sdk.NewInt64Coin("stake", 1), sdk.NewInt64Coin("stake", 10), false, false},
}
for tcIndex, tc := range cases {
tc := tc
if tc.panics {
s.Require().Panics(func() { tc.inputOne.IsEqual(tc.inputTwo) })
} else {
res := tc.inputOne.IsEqual(tc.inputTwo)
s.Require().Equal(tc.expected, res, "coin equality relation is incorrect, tc #%d", tcIndex)
}
}
}
func (s *coinTestSuite) TestCoinIsValid() {
loremIpsum := `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam viverra dui vel nulla aliquet, non dictum elit aliquam. Proin consequat leo in consectetur mattis. Phasellus eget odio luctus, rutrum dolor at, venenatis ante. Praesent metus erat, sodales vitae sagittis eget, commodo non ipsum. Duis eget urna quis erat mattis pulvinar. Vivamus egestas imperdiet sem, porttitor hendrerit lorem pulvinar in. Vivamus laoreet sapien eget libero euismod tristique. Suspendisse tincidunt nulla quis luctus mattis.
Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed id turpis at erat placerat fermentum id sed sapien. Fusce mattis enim id nulla viverra, eget placerat eros aliquet. Nunc fringilla urna ac condimentum ultricies. Praesent in eros ac neque fringilla sodales. Donec ut venenatis eros. Quisque iaculis lectus neque, a varius sem ullamcorper nec. Cras tincidunt dignissim libero nec volutpat. Donec molestie enim sed metus venenatis, quis elementum sem varius. Curabitur eu venenatis nulla.
Cras sit amet ligula vel turpis placerat sollicitudin. Nunc massa odio, eleifend id lacus nec, ultricies elementum arcu. Donec imperdiet nulla lacus, a venenatis lacus fermentum nec. Proin vestibulum dolor enim, vitae posuere velit aliquet non. Suspendisse pharetra condimentum nunc tincidunt viverra. Etiam posuere, ligula ut maximus congue, mauris orci consectetur velit, vel finibus eros metus non tellus. Nullam et dictum metus. Aliquam maximus fermentum mauris elementum aliquet. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Etiam dapibus lectus sed tellus rutrum tincidunt. Nulla at dolor sem. Ut non dictum arcu, eget congue sem.`
loremIpsum = strings.ReplaceAll(loremIpsum, " ", "")
loremIpsum = strings.ReplaceAll(loremIpsum, ".", "")
loremIpsum = strings.ReplaceAll(loremIpsum, ",", "")
cases := []struct {
coin sdk.Coin
expectPass bool
}{
{sdk.Coin{testDenom1, sdk.NewInt(-1)}, false},
{sdk.Coin{testDenom1, sdk.NewInt(0)}, true},
{sdk.Coin{testDenom1, sdk.OneInt()}, true},
{sdk.Coin{"Atom", sdk.OneInt()}, true},
{sdk.Coin{"ATOM", sdk.OneInt()}, true},
{sdk.Coin{"a", sdk.OneInt()}, false},
{sdk.Coin{loremIpsum, sdk.OneInt()}, false},
{sdk.Coin{"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdk.OneInt()}, true},
{sdk.Coin{"atOm", sdk.OneInt()}, true},
{sdk.Coin{" ", sdk.OneInt()}, false},
}
for i, tc := range cases {
s.Require().Equal(tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i)
}
}
func (s *coinTestSuite) TestCustomValidation() {
newDnmRegex := `[\x{1F600}-\x{1F6FF}]`
sdk.SetCoinDenomRegex(func() string {
return newDnmRegex
})
cases := []struct {
coin sdk.Coin
expectPass bool
}{
{sdk.Coin{"🙂", sdk.NewInt(1)}, true},
{sdk.Coin{"🙁", sdk.NewInt(1)}, true},
{sdk.Coin{"🌶", sdk.NewInt(1)}, false}, // outside the unicode range listed above
{sdk.Coin{"asdf", sdk.NewInt(1)}, false},
{sdk.Coin{"", sdk.NewInt(1)}, false},
}
for i, tc := range cases {
s.Require().Equal(tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i)
}
sdk.SetCoinDenomRegex(sdk.DefaultCoinDenomRegex)
}
func (s *coinTestSuite) TestAddCoin() {
cases := []struct {
inputOne sdk.Coin
inputTwo sdk.Coin
expected sdk.Coin
shouldPanic bool
}{
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 2), false},
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom1, 1), false},
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), sdk.NewInt64Coin(testDenom1, 1), true},
}
for tcIndex, tc := range cases {
tc := tc
if tc.shouldPanic {
s.Require().Panics(func() { tc.inputOne.Add(tc.inputTwo) })
} else {
res := tc.inputOne.Add(tc.inputTwo)
s.Require().Equal(tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex)
}
}
}
func (s *coinTestSuite) TestAddCoinAmount() {
cases := []struct {
coin sdk.Coin
amount sdk.Int
expected sdk.Coin
}{
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt(1), sdk.NewInt64Coin(testDenom1, 2)},
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt(0), sdk.NewInt64Coin(testDenom1, 1)},
}
for i, tc := range cases {
res := tc.coin.AddAmount(tc.amount)
s.Require().Equal(tc.expected, res, "result of addition is incorrect, tc #%d", i)
}
}
func (s *coinTestSuite) TestSubCoin() {
cases := []struct {
inputOne sdk.Coin
inputTwo sdk.Coin
expected sdk.Coin
shouldPanic bool
}{
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), sdk.NewInt64Coin(testDenom1, 1), true},
{sdk.NewInt64Coin(testDenom1, 10), sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 9), false},
{sdk.NewInt64Coin(testDenom1, 5), sdk.NewInt64Coin(testDenom1, 3), sdk.NewInt64Coin(testDenom1, 2), false},
{sdk.NewInt64Coin(testDenom1, 5), sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom1, 5), false},
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 5), sdk.Coin{}, true},
}
for tcIndex, tc := range cases {
tc := tc
if tc.shouldPanic {
s.Require().Panics(func() { tc.inputOne.Sub(tc.inputTwo) })
} else {
res := tc.inputOne.Sub(tc.inputTwo)
s.Require().Equal(tc.expected, res, "difference of coins is incorrect, tc #%d", tcIndex)
}
}
tc := struct {
inputOne sdk.Coin
inputTwo sdk.Coin
expected int64
}{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), 0}
res := tc.inputOne.Sub(tc.inputTwo)
s.Require().Equal(tc.expected, res.Amount.Int64())
}
func (s *coinTestSuite) TestSubCoinAmount() {
cases := []struct {
coin sdk.Coin
amount sdk.Int
expected sdk.Coin
shouldPanic bool
}{
{sdk.NewInt64Coin(testDenom1, 2), sdk.NewInt(1), sdk.NewInt64Coin(testDenom1, 1), false},
{sdk.NewInt64Coin(testDenom1, 10), sdk.NewInt(1), sdk.NewInt64Coin(testDenom1, 9), false},
{sdk.NewInt64Coin(testDenom1, 5), sdk.NewInt(3), sdk.NewInt64Coin(testDenom1, 2), false},
{sdk.NewInt64Coin(testDenom1, 5), sdk.NewInt(0), sdk.NewInt64Coin(testDenom1, 5), false},
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt(5), sdk.Coin{}, true},
}
for i, tc := range cases {
if tc.shouldPanic {
s.Require().Panics(func() { tc.coin.SubAmount(tc.amount) })
} else {
res := tc.coin.SubAmount(tc.amount)
s.Require().Equal(tc.expected, res, "result of subtraction is incorrect, tc #%d", i)
}
}
}
func (s *coinTestSuite) TestIsGTECoin() {
cases := []struct {
inputOne sdk.Coin
inputTwo sdk.Coin
expected bool
panics bool
}{
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), true, false},
{sdk.NewInt64Coin(testDenom1, 2), sdk.NewInt64Coin(testDenom1, 1), true, false},
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), false, true},
}
for tcIndex, tc := range cases {
tc := tc
if tc.panics {
s.Require().Panics(func() { tc.inputOne.IsGTE(tc.inputTwo) })
} else {
res := tc.inputOne.IsGTE(tc.inputTwo)
s.Require().Equal(tc.expected, res, "coin GTE relation is incorrect, tc #%d", tcIndex)
}
}
}
func (s *coinTestSuite) TestIsLTCoin() {
cases := []struct {
inputOne sdk.Coin
inputTwo sdk.Coin
expected bool
panics bool
}{
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), false, false},
{sdk.NewInt64Coin(testDenom1, 2), sdk.NewInt64Coin(testDenom1, 1), false, false},
{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1), false, true},
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), false, true},
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), false, false},
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 2), true, false},
}
for tcIndex, tc := range cases {
tc := tc
if tc.panics {
s.Require().Panics(func() { tc.inputOne.IsLT(tc.inputTwo) })
} else {
res := tc.inputOne.IsLT(tc.inputTwo)
s.Require().Equal(tc.expected, res, "coin LT relation is incorrect, tc #%d", tcIndex)
}
}
}
func (s *coinTestSuite) TestCoinIsZero() {
coin := sdk.NewInt64Coin(testDenom1, 0)
res := coin.IsZero()
s.Require().True(res)
coin = sdk.NewInt64Coin(testDenom1, 1)
res = coin.IsZero()
s.Require().False(res)
}
func (s *coinTestSuite) TestCoinIsNil() {
coin := sdk.Coin{}
res := coin.IsNil()
s.Require().True(res)
coin = sdk.Coin{Denom: "uatom"}
res = coin.IsNil()
s.Require().True(res)
coin = sdk.NewInt64Coin(testDenom1, 1)
res = coin.IsNil()
s.Require().False(res)
}
func (s *coinTestSuite) TestFilteredZeroCoins() {
cases := []struct {
name string
input sdk.Coins
original string
expected string
}{
{
name: "all greater than zero",
input: sdk.Coins{
{"testa", sdk.OneInt()},
{"testb", sdk.NewInt(2)},
{"testc", sdk.NewInt(3)},
{"testd", sdk.NewInt(4)},
{"teste", sdk.NewInt(5)},
},
original: "1testa,2testb,3testc,4testd,5teste",
expected: "1testa,2testb,3testc,4testd,5teste",
},
{
name: "zero coin in middle",
input: sdk.Coins{
{"testa", sdk.OneInt()},
{"testb", sdk.NewInt(2)},
{"testc", sdk.NewInt(0)},
{"testd", sdk.NewInt(4)},
{"teste", sdk.NewInt(5)},
},
original: "1testa,2testb,0testc,4testd,5teste",
expected: "1testa,2testb,4testd,5teste",
},
{
name: "zero coin end (unordered)",
input: sdk.Coins{
{"teste", sdk.NewInt(5)},
{"testc", sdk.NewInt(3)},
{"testa", sdk.OneInt()},
{"testd", sdk.NewInt(4)},
{"testb", sdk.NewInt(0)},
},
original: "5teste,3testc,1testa,4testd,0testb",
expected: "1testa,3testc,4testd,5teste",
},
}
for _, tt := range cases {
undertest := sdk.NewCoins(tt.input...)
s.Require().Equal(tt.expected, undertest.String(), "NewCoins must return expected results")
s.Require().Equal(tt.original, tt.input.String(), "input must be unmodified and match original")
}
}
// ----------------------------------------------------------------------------
// Coins tests
func (s *coinTestSuite) TestCoins_String() {
cases := []struct {
name string
input sdk.Coins
expected string
}{
{
"empty coins",
sdk.Coins{},
"",
},
{
"single coin",
sdk.Coins{{"tree", sdk.OneInt()}},
"1tree",
},
{
"multiple coins",
sdk.Coins{
{"tree", sdk.OneInt()},
{"gas", sdk.OneInt()},
{"mineral", sdk.OneInt()},
},
"1tree,1gas,1mineral",
},
}
for _, tt := range cases {
s.Require().Equal(tt.expected, tt.input.String())
}
}
func (s *coinTestSuite) TestIsZeroCoins() {
cases := []struct {
inputOne sdk.Coins
expected bool
}{
{sdk.Coins{}, true},
{sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, true},
{sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 0)}, true},
{sdk.Coins{sdk.NewInt64Coin(testDenom1, 1)}, false},
{sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, false},
}
for _, tc := range cases {
res := tc.inputOne.IsZero()
s.Require().Equal(tc.expected, res)
}
}
func (s *coinTestSuite) TestEqualCoins() {
cases := []struct {
inputOne sdk.Coins
inputTwo sdk.Coins
expected bool
panics bool
}{
{sdk.Coins{}, sdk.Coins{}, true, false},
{sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, true, false},
{sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, true, false},
{sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, sdk.Coins{sdk.NewInt64Coin(testDenom2, 0)}, false, true},
{sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 1)}, false, false},
{sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, false, false},
{sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, true, false},
}
for tcnum, tc := range cases {
tc := tc
if tc.panics {
s.Require().Panics(func() { tc.inputOne.IsEqual(tc.inputTwo) })
} else {
res := tc.inputOne.IsEqual(tc.inputTwo)
s.Require().Equal(tc.expected, res, "Equality is differed from exported. tc #%d, expected %b, actual %b.", tcnum, tc.expected, res)
}
}
}
func (s *coinTestSuite) TestAddCoins() {
cases := []struct {
inputOne sdk.Coins
inputTwo sdk.Coins
expected sdk.Coins
}{
{sdk.Coins{s.ca1, s.cm1}, sdk.Coins{s.ca1, s.cm1}, sdk.Coins{s.ca2, s.cm2}},
{sdk.Coins{s.ca0, s.cm1}, sdk.Coins{s.ca0, s.cm0}, sdk.Coins{s.cm1}},
{sdk.Coins{s.ca2}, sdk.Coins{s.cm0}, sdk.Coins{s.ca2}},
{sdk.Coins{s.ca1}, sdk.Coins{s.ca1, s.cm2}, sdk.Coins{s.ca2, s.cm2}},
{sdk.Coins{s.ca0, s.cm0}, sdk.Coins{s.ca0, s.cm0}, sdk.Coins(nil)},
}
for tcIndex, tc := range cases {
res := tc.inputOne.Add(tc.inputTwo...)
s.Require().True(res.IsValid())
s.Require().Equal(tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex)
}
}
func (s *coinTestSuite) TestSubCoins() {
testCases := []struct {
inputOne sdk.Coins
inputTwo sdk.Coins
expected sdk.Coins
shouldPanic bool
}{
// denoms are not sorted - should panic
{sdk.Coins{s.ca2}, sdk.Coins{s.cm2, s.ca1}, sdk.Coins{}, true},
{sdk.Coins{s.cm2, s.ca2}, sdk.Coins{s.ca1}, sdk.Coins{}, true},
// test cases for sorted denoms
{sdk.Coins{s.ca2}, sdk.Coins{s.ca1, s.cm2}, sdk.Coins{s.ca1, s.cm2}, true},
{sdk.Coins{s.ca2}, sdk.Coins{s.cm0}, sdk.Coins{s.ca2}, false},
{sdk.Coins{s.ca1}, sdk.Coins{s.cm0}, sdk.Coins{s.ca1}, false},
{sdk.Coins{s.ca1, s.cm1}, sdk.Coins{s.ca1}, sdk.Coins{s.cm1}, false},
{sdk.Coins{s.ca1, s.cm1}, sdk.Coins{s.ca2}, sdk.Coins{}, true},
}
assert := s.Assert()
for i, tc := range testCases {
tc := tc
if tc.shouldPanic {
assert.Panics(func() { tc.inputOne.Sub(tc.inputTwo) })
} else {
res := tc.inputOne.Sub(tc.inputTwo)
assert.True(res.IsValid())
assert.Equal(tc.expected, res, "sum of coins is incorrect, tc #%d", i)
}
}
}
func (s *coinTestSuite) TestCoins_Validate() {
testCases := []struct {
name string
coins sdk.Coins
expPass bool
}{
{
"valid lowercase coins",
sdk.Coins{
{"gas", sdk.OneInt()},
{"mineral", sdk.OneInt()},
{"tree", sdk.OneInt()},
},
true,
},
{
"valid uppercase coins",
sdk.Coins{
{"GAS", sdk.OneInt()},
{"MINERAL", sdk.OneInt()},
{"TREE", sdk.OneInt()},
},
true,
},
{
"valid uppercase coin",
sdk.Coins{
{"ATOM", sdk.OneInt()},
},
true,
},
{
"valid lower and uppercase coins (1)",
sdk.Coins{
{"GAS", sdk.OneInt()},
{"gAs", sdk.OneInt()},
},
true,
},
{
"valid lower and uppercase coins (2)",
sdk.Coins{
{"ATOM", sdk.OneInt()},
{"Atom", sdk.OneInt()},
{"atom", sdk.OneInt()},
},
true,
},
{
"mixed case (1)",
sdk.Coins{
{"MineraL", sdk.OneInt()},
{"TREE", sdk.OneInt()},
{"gAs", sdk.OneInt()},
},
true,
},
{
"mixed case (2)",
sdk.Coins{
{"gAs", sdk.OneInt()},
{"mineral", sdk.OneInt()},
},
true,
},
{
"mixed case (3)",
sdk.Coins{
{"gAs", sdk.OneInt()},
},
true,
},
{
"unicode letters and numbers",
sdk.Coins{
{"𐀀𐀆𐀉Ⅲ", sdk.OneInt()},
},
false,
},
{
"emojis",
sdk.Coins{
{"🤑😋🤔", sdk.OneInt()},
},
false,
},
{
"IBC denominations (ADR 001)",
sdk.Coins{
{"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdk.OneInt()},
{"ibc/876563AAAACF739EB061C67CDB5EDF2B7C9FD4AA9D876450CC21210807C2820A", sdk.NewInt(2)},
},
true,
},
{
"empty (1)",
sdk.NewCoins(),
true,
},
{
"empty (2)",
sdk.Coins{},
true,
},
{
"invalid denomination (1)",
sdk.Coins{
{"MineraL", sdk.OneInt()},
{"0TREE", sdk.OneInt()},
{"gAs", sdk.OneInt()},
},
false,
},
{
"invalid denomination (2)",
sdk.Coins{
{"-GAS", sdk.OneInt()},
{"gAs", sdk.OneInt()},
},
false,
},
{
"bad sort (1)",
sdk.Coins{
{"tree", sdk.OneInt()},
{"gas", sdk.OneInt()},
{"mineral", sdk.OneInt()},
},
false,
},
{
"bad sort (2)",
sdk.Coins{
{"gas", sdk.OneInt()},
{"tree", sdk.OneInt()},
{"mineral", sdk.OneInt()},
},
false,
},
{
"non-positive amount (1)",
sdk.Coins{
{"gas", sdk.OneInt()},
{"tree", sdk.NewInt(0)},
{"mineral", sdk.OneInt()},
},
false,
},
{
"non-positive amount (2)",
sdk.Coins{
{"gas", sdk.NewInt(-1)},
{"tree", sdk.OneInt()},
{"mineral", sdk.OneInt()},
},
false,
}, {
"duplicate denomination",
sdk.Coins{
{"gas", sdk.OneInt()},
{"gas", sdk.OneInt()},
{"mineral", sdk.OneInt()},
},
false,
},
}
for _, tc := range testCases {
err := tc.coins.Validate()
if tc.expPass {
s.Require().NoError(err, tc.name)
} else {
s.Require().Error(err, tc.name)
}
}
}
func (s *coinTestSuite) TestMinMax() {
one := sdk.OneInt()
two := sdk.NewInt(2)
cases := []struct {
name string
input1 sdk.Coins
input2 sdk.Coins
min sdk.Coins
max sdk.Coins
}{
{"zero-zero", sdk.Coins{}, sdk.Coins{}, sdk.Coins{}, sdk.Coins{}},
{"zero-one", sdk.Coins{}, sdk.Coins{{testDenom1, one}}, sdk.Coins{}, sdk.Coins{{testDenom1, one}}},
{"two-zero", sdk.Coins{{testDenom2, two}}, sdk.Coins{}, sdk.Coins{}, sdk.Coins{{testDenom2, two}}},
{"disjoint", sdk.Coins{{testDenom1, one}}, sdk.Coins{{testDenom2, two}}, sdk.Coins{}, sdk.Coins{{testDenom1, one}, {testDenom2, two}}},
{"overlap", sdk.Coins{{testDenom1, one}, {testDenom2, two}}, sdk.Coins{{testDenom1, two}, {testDenom2, one}},
sdk.Coins{{testDenom1, one}, {testDenom2, one}}, sdk.Coins{{testDenom1, two}, {testDenom2, two}}},
}
for _, tc := range cases {
min := tc.input1.Min(tc.input2)
max := tc.input1.Max(tc.input2)
s.Require().True(min.IsEqual(tc.min), tc.name)
s.Require().True(max.IsEqual(tc.max), tc.name)
}
}
func (s *coinTestSuite) TestCoinsGT() {
one := sdk.OneInt()
two := sdk.NewInt(2)
s.Require().False(sdk.Coins{}.IsAllGT(sdk.Coins{}))
s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{}))
s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom1, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom2, one}}))
s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(sdk.Coins{{testDenom2, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGT(sdk.Coins{{testDenom2, two}}))
}
func (s *coinTestSuite) TestCoinsLT() {
one := sdk.OneInt()
two := sdk.NewInt(2)
s.Require().False(sdk.Coins{}.IsAllLT(sdk.Coins{}))
s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLT(sdk.Coins{}))
s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLT(sdk.Coins{{testDenom1, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLT(sdk.Coins{{testDenom2, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(sdk.Coins{{testDenom2, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(sdk.Coins{{testDenom2, two}}))
s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(sdk.Coins{{testDenom1, one}, {testDenom2, one}}))
s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(sdk.Coins{{testDenom1, two}, {testDenom2, two}}))
s.Require().True(sdk.Coins{}.IsAllLT(sdk.Coins{{testDenom1, one}}))
}
func (s *coinTestSuite) TestCoinsLTE() {
one := sdk.OneInt()
two := sdk.NewInt(2)
s.Require().True(sdk.Coins{}.IsAllLTE(sdk.Coins{}))
s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLTE(sdk.Coins{}))
s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllLTE(sdk.Coins{{testDenom1, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLTE(sdk.Coins{{testDenom2, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(sdk.Coins{{testDenom2, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(sdk.Coins{{testDenom2, two}}))
s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(sdk.Coins{{testDenom1, one}, {testDenom2, one}}))
s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}}))
s.Require().True(sdk.Coins{}.IsAllLTE(sdk.Coins{{testDenom1, one}}))
}
func (s *coinTestSuite) TestParseCoins() {
one := sdk.OneInt()
cases := []struct {
input string
valid bool // if false, we expect an error on parse
expected sdk.Coins // if valid is true, make sure this is returned
}{
{"", true, nil},
{"0stake", true, sdk.Coins{}}, // remove zero coins
{"0stake,1foo,99bar", true, sdk.Coins{{"bar", sdk.NewInt(99)}, {"foo", one}}}, // remove zero coins
{"1foo", true, sdk.Coins{{"foo", one}}},
{"10btc,1atom,20btc", false, nil},
{"10bar", true, sdk.Coins{{"bar", sdk.NewInt(10)}}},
{"99bar,1foo", true, sdk.Coins{{"bar", sdk.NewInt(99)}, {"foo", one}}},
{"98 bar , 1 foo ", true, sdk.Coins{{"bar", sdk.NewInt(98)}, {"foo", one}}},
{" 55\t \t bling\n", true, sdk.Coins{{"bling", sdk.NewInt(55)}}},
{"2foo, 97 bar", true, sdk.Coins{{"bar", sdk.NewInt(97)}, {"foo", sdk.NewInt(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", true, sdk.Coins{{"btc", sdk.NewInt(1)}}}, // amount can be decimal, will get truncated
{"5foo:bar", false, nil}, // invalid separator
{"10atom10", true, sdk.Coins{{"atom10", sdk.NewInt(10)}}},
{"200transfer/channelToA/uatom", true, sdk.Coins{{"transfer/channelToA/uatom", sdk.NewInt(200)}}},
{"50ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", true, sdk.Coins{{"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdk.NewInt(50)}}},
{"120000000000000000000000000000000000000000000000000000000000000000000000000000btc", false, nil},
}
for tcIndex, tc := range cases {
res, err := sdk.ParseCoinsNormalized(tc.input)
if !tc.valid {
s.Require().Error(err, "%s: %#v. tc #%d", tc.input, res, tcIndex)
} else if s.Assert().Nil(err, "%s: %+v", tc.input, err) {
s.Require().Equal(tc.expected, res, "coin parsing was incorrect, tc #%d", tcIndex)
}
}
}
func (s *coinTestSuite) TestSortCoins() {
good := sdk.Coins{
sdk.NewInt64Coin("gas", 1),
sdk.NewInt64Coin("mineral", 1),
sdk.NewInt64Coin("tree", 1),
}
empty := sdk.Coins{
sdk.NewInt64Coin("gold", 0),
}
badSort1 := sdk.Coins{
sdk.NewInt64Coin("tree", 1),
sdk.NewInt64Coin("gas", 1),
sdk.NewInt64Coin("mineral", 1),
}
badSort2 := sdk.Coins{ // both are after the first one, but the second and third are in the wrong order
sdk.NewInt64Coin("gas", 1),
sdk.NewInt64Coin("tree", 1),
sdk.NewInt64Coin("mineral", 1),
}
badAmt := sdk.Coins{
sdk.NewInt64Coin("gas", 1),
sdk.NewInt64Coin("tree", 0),
sdk.NewInt64Coin("mineral", 1),
}
dup := sdk.Coins{
sdk.NewInt64Coin("gas", 1),
sdk.NewInt64Coin("gas", 1),
sdk.NewInt64Coin("mineral", 1),
}
cases := []struct {
name string
coins sdk.Coins
validBefore,
validAfter bool
}{
{"valid coins", good, true, true},
{"empty coins", empty, false, false},
{"bad sort (1)", badSort1, false, true},
{"bad sort (2)", badSort2, false, true},
{"zero value coin", badAmt, false, false},
{"duplicate coins", dup, false, false},
}
for _, tc := range cases {
err := tc.coins.Validate()
if tc.validBefore {
s.Require().NoError(err, tc.name)
} else {
s.Require().Error(err, tc.name)
}
tc.coins.Sort()
err = tc.coins.Validate()
if tc.validAfter {
s.Require().NoError(err, tc.name)
} else {
s.Require().Error(err, tc.name)
}
}
}
func (s *coinTestSuite) TestAmountOf() {
case0 := sdk.Coins{}
case1 := sdk.Coins{
sdk.NewInt64Coin("gold", 0),
}
case2 := sdk.Coins{
sdk.NewInt64Coin("gas", 1),
sdk.NewInt64Coin("mineral", 1),
sdk.NewInt64Coin("tree", 1),
}
case3 := sdk.Coins{
sdk.NewInt64Coin("mineral", 1),
sdk.NewInt64Coin("tree", 1),
}
case4 := sdk.Coins{
sdk.NewInt64Coin("gas", 8),
}
cases := []struct {
coins sdk.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, 1, 1, 1},
{case3, 0, 0, 0, 1, 1},
{case4, 0, 0, 8, 0, 0},
}
for _, tc := range cases {
s.Require().Equal(sdk.NewInt(tc.amountOfGAS), tc.coins.AmountOf("gas"))
s.Require().Equal(sdk.NewInt(tc.amountOfMINERAL), tc.coins.AmountOf("mineral"))
s.Require().Equal(sdk.NewInt(tc.amountOfTREE), tc.coins.AmountOf("tree"))
}
s.Require().Panics(func() { cases[0].coins.AmountOf("10Invalid") })
}
func (s *coinTestSuite) TestCoinsIsAnyGTE() {
one := sdk.OneInt()
two := sdk.NewInt(2)
s.Require().False(sdk.Coins{}.IsAnyGTE(sdk.Coins{}))
s.Require().False(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{}))
s.Require().False(sdk.Coins{}.IsAnyGTE(sdk.Coins{{testDenom1, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{{testDenom1, two}}))
s.Require().False(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{{testDenom2, one}}))
s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAnyGTE(sdk.Coins{{testDenom1, two}, {testDenom2, one}}))
s.Require().True(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{{testDenom1, one}}))
s.Require().True(sdk.Coins{{testDenom1, two}}.IsAnyGTE(sdk.Coins{{testDenom1, one}}))
s.Require().True(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}}))
s.Require().True(sdk.Coins{{testDenom2, two}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}}))
s.Require().False(sdk.Coins{{testDenom2, one}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}}))
s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, one}}))
s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}}))
s.Require().True(sdk.Coins{{"xxx", one}, {"yyy", one}}.IsAnyGTE(sdk.Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}}))
}
func (s *coinTestSuite) TestCoinsIsAllGT() {
one := sdk.OneInt()
two := sdk.NewInt(2)
s.Require().False(sdk.Coins{}.IsAllGT(sdk.Coins{}))
s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{}))
s.Require().False(sdk.Coins{}.IsAllGT(sdk.Coins{{testDenom1, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom1, two}}))
s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom2, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(sdk.Coins{{testDenom1, two}, {testDenom2, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom1, one}}))
s.Require().True(sdk.Coins{{testDenom1, two}}.IsAllGT(sdk.Coins{{testDenom1, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, two}}))
s.Require().False(sdk.Coins{{testDenom2, two}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, two}}))
s.Require().False(sdk.Coins{{testDenom2, one}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, two}}))
s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, two}}))
s.Require().False(sdk.Coins{{"xxx", one}, {"yyy", one}}.IsAllGT(sdk.Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}}))
}
func (s *coinTestSuite) TestCoinsIsAllGTE() {
one := sdk.OneInt()
two := sdk.NewInt(2)
s.Require().True(sdk.Coins{}.IsAllGTE(sdk.Coins{}))
s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{}))
s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(sdk.Coins{{testDenom2, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(sdk.Coins{{testDenom2, two}}))
s.Require().False(sdk.Coins{}.IsAllGTE(sdk.Coins{{testDenom1, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{{testDenom1, two}}))
s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{{testDenom2, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGTE(sdk.Coins{{testDenom1, two}, {testDenom2, one}}))
s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{{testDenom1, one}}))
s.Require().True(sdk.Coins{{testDenom1, two}}.IsAllGTE(sdk.Coins{{testDenom1, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}}))
s.Require().False(sdk.Coins{{testDenom2, two}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}}))
s.Require().False(sdk.Coins{{testDenom2, one}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}}))
s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, one}}))
s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}}))
s.Require().False(sdk.Coins{{"xxx", one}, {"yyy", one}}.IsAllGTE(sdk.Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}}))
}
func (s *coinTestSuite) TestNewCoins() {
tenatom := sdk.NewInt64Coin("atom", 10)
tenbtc := sdk.NewInt64Coin("btc", 10)
zeroeth := sdk.NewInt64Coin("eth", 0)
invalidCoin := sdk.Coin{"0ETH", sdk.OneInt()}
tests := []struct {
name string
coins sdk.Coins
want sdk.Coins
wantPanic bool
}{
{"empty args", []sdk.Coin{}, sdk.Coins{}, false},
{"one coin", []sdk.Coin{tenatom}, sdk.Coins{tenatom}, false},
{"sort after create", []sdk.Coin{tenbtc, tenatom}, sdk.Coins{tenatom, tenbtc}, false},
{"sort and remove zeroes", []sdk.Coin{zeroeth, tenbtc, tenatom}, sdk.Coins{tenatom, tenbtc}, false},
{"panic on dups", []sdk.Coin{tenatom, tenatom}, sdk.Coins{}, true},
{"panic on invalid coin", []sdk.Coin{invalidCoin, tenatom}, sdk.Coins{}, true},
}
for _, tt := range tests {
if tt.wantPanic {
s.Require().Panics(func() { sdk.NewCoins(tt.coins...) })
continue
}
got := sdk.NewCoins(tt.coins...)
s.Require().True(got.IsEqual(tt.want))
}
}
func (s *coinTestSuite) TestCoinsIsAnyGT() {
twoAtom := sdk.NewInt64Coin("atom", 2)
fiveAtom := sdk.NewInt64Coin("atom", 5)
threeEth := sdk.NewInt64Coin("eth", 3)
sixEth := sdk.NewInt64Coin("eth", 6)
twoBtc := sdk.NewInt64Coin("btc", 2)
tests := []struct {
name string
coinsA sdk.Coins
coinsB sdk.Coins
expPass bool
}{
{"{} ≤ {}", sdk.Coins{}, sdk.Coins{}, false},
{"{} ≤ 5atom", sdk.Coins{}, sdk.Coins{fiveAtom}, false},
{"5atom > 2atom", sdk.Coins{fiveAtom}, sdk.Coins{twoAtom}, true},
{"2atom ≤ 5atom", sdk.Coins{twoAtom}, sdk.Coins{fiveAtom}, false},
{"2atom,6eth > 2btc,5atom,3eth", sdk.Coins{twoAtom, sixEth}, sdk.Coins{twoBtc, fiveAtom, threeEth}, true},
{"2btc,2atom,3eth ≤ 5atom,6eth", sdk.Coins{twoBtc, twoAtom, threeEth}, sdk.Coins{fiveAtom, sixEth}, false},
{"2atom,6eth ≤ 2btc,5atom", sdk.Coins{twoAtom, sixEth}, sdk.Coins{twoBtc, fiveAtom}, false},
}
for _, tc := range tests {
s.Require().True(tc.expPass == tc.coinsA.IsAnyGT(tc.coinsB), tc.name)
}
}
func (s *coinTestSuite) TestCoinsIsAnyNil() {
twoAtom := sdk.NewInt64Coin("atom", 2)
fiveAtom := sdk.NewInt64Coin("atom", 5)
threeEth := sdk.NewInt64Coin("eth", 3)
nilAtom := sdk.Coin{Denom: "atom"}
s.Require().True(sdk.Coins{twoAtom, fiveAtom, threeEth, nilAtom}.IsAnyNil())
s.Require().True(sdk.Coins{twoAtom, nilAtom, fiveAtom, threeEth}.IsAnyNil())
s.Require().True(sdk.Coins{nilAtom, twoAtom, fiveAtom, threeEth}.IsAnyNil())
s.Require().False(sdk.Coins{twoAtom, fiveAtom, threeEth}.IsAnyNil())
}
func (s *coinTestSuite) TestMarshalJSONCoins() {
cdc := codec.NewLegacyAmino()
sdk.RegisterLegacyAminoCodec(cdc)
testCases := []struct {
name string
input sdk.Coins
strOutput string
}{
{"nil coins", nil, `[]`},
{"empty coins", sdk.Coins{}, `[]`},
{"non-empty coins", sdk.NewCoins(sdk.NewInt64Coin("foo", 50)), `[{"denom":"foo","amount":"50"}]`},
}
for _, tc := range testCases {
bz, err := cdc.MarshalJSON(tc.input)
s.Require().NoError(err)
s.Require().Equal(tc.strOutput, string(bz))
var newCoins sdk.Coins
s.Require().NoError(cdc.UnmarshalJSON(bz, &newCoins))
if tc.input.Empty() {
s.Require().Nil(newCoins)
} else {
s.Require().Equal(tc.input, newCoins)
}
}
}
func (s *coinTestSuite) TestCoinAminoEncoding() {
cdc := codec.NewLegacyAmino()
c := sdk.NewInt64Coin(testDenom1, 5)
bz1, err := cdc.Marshal(c)
s.Require().NoError(err)
bz2, err := cdc.MarshalLengthPrefixed(c)
s.Require().NoError(err)
bz3, err := c.Marshal()
s.Require().NoError(err)
s.Require().Equal(bz1, bz3)
s.Require().Equal(bz2[1:], bz3)
}