diff --git a/types/coin.go b/types/coin.go index 96b9c71b2dc6..ad287a0a785b 100644 --- a/types/coin.go +++ b/types/coin.go @@ -546,18 +546,15 @@ func (coins Coins) negative() Coins { // removeZeroCoins removes all zero coins from the given coin set in-place. func removeZeroCoins(coins Coins) Coins { - i, l := 0, len(coins) - for i < l { - if coins[i].IsZero() { - // remove coin - coins = append(coins[:i], coins[i+1:]...) - l-- - } else { - i++ + result := make([]Coin, 0, len(coins)) + + for _, coin := range coins { + if !coin.IsZero() { + result = append(result, coin) } } - return coins[:i] + return result } //----------------------------------------------------------------------------- diff --git a/types/coin_test.go b/types/coin_test.go index 7177a180b0b9..9d896861ab8a 100644 --- a/types/coin_test.go +++ b/types/coin_test.go @@ -191,6 +191,61 @@ func TestCoinIsZero(t *testing.T) { require.False(t, res) } +func TestFilteredZeroCoins(t *testing.T) { + cases := []struct { + name string + input Coins + original string + expected string + }{ + { + name: "all greater than zero", + input: Coins{ + {"testa", NewInt(1)}, + {"testb", NewInt(2)}, + {"testc", NewInt(3)}, + {"testd", NewInt(4)}, + {"teste", NewInt(5)}, + }, + original: "1testa,2testb,3testc,4testd,5teste", + expected: "1testa,2testb,3testc,4testd,5teste", + }, + { + name: "zero coin in middle", + input: Coins{ + {"testa", NewInt(1)}, + {"testb", NewInt(2)}, + {"testc", NewInt(0)}, + {"testd", NewInt(4)}, + {"teste", NewInt(5)}, + }, + original: "1testa,2testb,0testc,4testd,5teste", + expected: "1testa,2testb,4testd,5teste", + }, + { + name: "zero coin end (unordered)", + input: Coins{ + {"teste", NewInt(5)}, + {"testc", NewInt(3)}, + {"testa", NewInt(1)}, + {"testd", NewInt(4)}, + {"testb", NewInt(0)}, + }, + original: "5teste,3testc,1testa,4testd,0testb", + expected: "1testa,3testc,4testd,5teste", + }, + } + + for _, tt := range cases { + tt := tt + t.Run(tt.name, func(t *testing.T) { + undertest := NewCoins(tt.input...) + require.Equal(t, tt.expected, undertest.String(), "NewCoins must return expected results") + require.Equal(t, tt.original, tt.input.String(), "input must be unmodified and match original") + }) + } +} + // ---------------------------------------------------------------------------- // Coins tests diff --git a/types/dec_coin.go b/types/dec_coin.go index 4b3396ca871d..128db1cae311 100644 --- a/types/dec_coin.go +++ b/types/dec_coin.go @@ -556,18 +556,15 @@ func (coins DecCoins) IsAllPositive() bool { } func removeZeroDecCoins(coins DecCoins) DecCoins { - i, l := 0, len(coins) - for i < l { - if coins[i].IsZero() { - // remove coin - coins = append(coins[:i], coins[i+1:]...) - l-- - } else { - i++ + result := make([]DecCoin, 0, len(coins)) + + for _, coin := range coins { + if !coin.IsZero() { + result = append(result, coin) } } - return coins[:i] + return result } //----------------------------------------------------------------------------- diff --git a/types/dec_coin_test.go b/types/dec_coin_test.go index 2a20bd6469f4..598ea2f48610 100644 --- a/types/dec_coin_test.go +++ b/types/dec_coin_test.go @@ -96,6 +96,61 @@ func TestAddDecCoins(t *testing.T) { } } +func TestFilteredZeroDecCoins(t *testing.T) { + cases := []struct { + name string + input DecCoins + original string + expected string + }{ + { + name: "all greater than zero", + input: DecCoins{ + {"testa", NewDec(1)}, + {"testb", NewDec(2)}, + {"testc", NewDec(3)}, + {"testd", NewDec(4)}, + {"teste", NewDec(5)}, + }, + original: "1.000000000000000000testa,2.000000000000000000testb,3.000000000000000000testc,4.000000000000000000testd,5.000000000000000000teste", + expected: "1.000000000000000000testa,2.000000000000000000testb,3.000000000000000000testc,4.000000000000000000testd,5.000000000000000000teste", + }, + { + name: "zero coin in middle", + input: DecCoins{ + {"testa", NewDec(1)}, + {"testb", NewDec(2)}, + {"testc", NewDec(0)}, + {"testd", NewDec(4)}, + {"teste", NewDec(5)}, + }, + original: "1.000000000000000000testa,2.000000000000000000testb,0.000000000000000000testc,4.000000000000000000testd,5.000000000000000000teste", + expected: "1.000000000000000000testa,2.000000000000000000testb,4.000000000000000000testd,5.000000000000000000teste", + }, + { + name: "zero coin end (unordered)", + input: DecCoins{ + {"teste", NewDec(5)}, + {"testc", NewDec(3)}, + {"testa", NewDec(1)}, + {"testd", NewDec(4)}, + {"testb", NewDec(0)}, + }, + original: "5.000000000000000000teste,3.000000000000000000testc,1.000000000000000000testa,4.000000000000000000testd,0.000000000000000000testb", + expected: "1.000000000000000000testa,3.000000000000000000testc,4.000000000000000000testd,5.000000000000000000teste", + }, + } + + for _, tt := range cases { + tt := tt + t.Run(tt.name, func(t *testing.T) { + undertest := NewDecCoins(tt.input...) + require.Equal(t, tt.expected, undertest.String(), "NewDecCoins must return expected results") + require.Equal(t, tt.original, tt.input.String(), "input must be unmodified and match original") + }) + } +} + func TestIsValid(t *testing.T) { tests := []struct { coin DecCoin