From 03bdd3f870946b996da1e316a5ee05560e508ec6 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Wed, 9 Jan 2019 15:31:03 -0500 Subject: [PATCH] Fix IsAnyGTE (#3265) --- cmd/gaia/cli_test/cli_test.go | 33 ++++++------- types/coin.go | 93 +++++++++++++---------------------- types/coin_test.go | 16 ++---- 3 files changed, 54 insertions(+), 88 deletions(-) diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index c663dd06a2..8f83aabd55 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -45,23 +45,22 @@ func TestGaiaCLIMinimumFees(t *testing.T) { require.False(f.T, success) tests.WaitForNextNBlocksTM(1, f.Port) - // TODO: Make this work - // // Ensure tx w/ correct fees (stake) pass - // txFees := fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(denom, 23)) - // success = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(denom, 10), txFees) - // require.True(f.T, success) - // tests.WaitForNextNBlocksTM(1, f.Port) - // - // // Ensure tx w/ correct fees (feetoken) pass - // txFees = fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 23)) - // success = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(feeDenom, 10), txFees) - // require.True(f.T, success) - // tests.WaitForNextNBlocksTM(1, f.Port) - // - // // Ensure tx w/ improper fees (footoken) fails - // txFees = fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(fooDenom, 23)) - // success = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 10), txFees) - // require.False(f.T, success) + // Ensure tx w/ correct fees (stake) pass + txFees := fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(denom, 23)) + success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(denom, 10), txFees) + require.True(f.T, success) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure tx w/ correct fees (feetoken) pass + txFees = fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 23)) + success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(feeDenom, 10), txFees) + require.True(f.T, success) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure tx w/ improper fees (footoken) fails + txFees = fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(fooDenom, 23)) + success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 10), txFees) + require.False(f.T, success) // Cleanup testing directories f.Cleanup() diff --git a/types/coin.go b/types/coin.go index c3f2ecfbf5..a3cbf6b7d4 100644 --- a/types/coin.go +++ b/types/coin.go @@ -265,28 +265,6 @@ func (coins Coins) SafeMinus(coinsB Coins) (Coins, bool) { return diff, !diff.IsNotNegative() } -// IsAnyGT returns true if coins contains at least one denom -// that is present at a smaller amount in coinsB; it -// returns false otherwise. -func (coins Coins) IsAnyGT(coinsB Coins) bool { - intersection := coins.intersect(coinsB) - if intersection.Empty() { - return false - } - - diff, _ := intersection.SafeMinus(coinsB) - if len(diff) == 0 { - return false - } - - for _, coin := range diff { - if coin.IsPositive() { - return true - } - } - return false -} - // IsAllGT returns true if for every denom in coins, the denom is present at a // greater amount in coinsB. func (coins Coins) IsAllGT(coinsB Coins) bool { @@ -298,28 +276,6 @@ func (coins Coins) IsAllGT(coinsB Coins) bool { return diff.IsPositive() } -// IsAnyGT returns true if coins contains at least one denom -// that is present at a smaller or equal amount in coinsB; it -// returns false otherwise. -func (coins Coins) IsAnyGTE(coinsB Coins) bool { - intersection := coins.intersect(coinsB) - if intersection.Empty() { - return false - } - - diff, _ := intersection.SafeMinus(coinsB) - if len(diff) == 0 || len(diff) < len(intersection) { // zero diff is removed from the diff set - return true - } - - for _, coin := range diff { - if coin.IsNotNegative() { - return true - } - } - return false -} - // IsAllGTE returns true iff for every denom in coins, the denom is present at // an equal or greater amount in coinsB. func (coins Coins) IsAllGTE(coinsB Coins) bool { @@ -343,6 +299,41 @@ func (coins Coins) IsAllLTE(coinsB Coins) bool { return coinsB.IsAllGTE(coins) } +// IsAnyGTE returns true iff coins contains at least one denom that is present +// at a greater or equal amount in coinsB; it returns false otherwise. +// +// NOTE: IsAnyGTE operates under the invariant that coins are sorted by +// denominations. +func (coins Coins) IsAnyGTE(coinsB Coins) bool { + if len(coinsB) == 0 { + return false + } + + j := 0 + for _, coin := range coins { + searchOther := true // terminator in case coins breaks the sorted invariant + + for j < len(coinsB) && searchOther { + switch strings.Compare(coin.Denom, coinsB[j].Denom) { + case -1: + // coin denom in less than the current other coin, so move to next coin + searchOther = false + case 0: + if coin.IsGTE(coinsB[j]) { + return true + } + + fallthrough // skip to next other coin + case 1: + // coin denom is greater than the current other coin, so move to next other coin + j++ + } + } + } + + return false +} + // IsZero returns true if there are no coins or all coins are zero. func (coins Coins) IsZero() bool { for _, coin := range coins { @@ -474,20 +465,6 @@ func removeZeroCoins(coins Coins) Coins { return coins[:i] } -func (coins Coins) intersect(coinsB Coins) Coins { - intersection := Coins{} - for _, coin := range coins { - for _, bCoin := range coinsB { - if coin.Denom == bCoin.Denom { - intersection = append(intersection, coin) - break - } - } - } - - return intersection -} - //----------------------------------------------------------------------------- // Sort interface diff --git a/types/coin_test.go b/types/coin_test.go index f82e3c110d..fb2d67acac 100644 --- a/types/coin_test.go +++ b/types/coin_test.go @@ -368,28 +368,18 @@ func TestCoinsLTE(t *testing.T) { assert.True(t, Coins{}.IsAllLTE(Coins{{"a", one}})) } -func TestCoinsIsAnyGT(t *testing.T) { - one := NewInt(1) - two := NewInt(2) - assert.False(t, Coins{}.IsAnyGT(Coins{})) - assert.False(t, Coins{{"a", one}}.IsAnyGT(Coins{})) - assert.False(t, Coins{}.IsAnyGT(Coins{{"a", one}})) - assert.False(t, Coins{{"a", one}}.IsAnyGT(Coins{{"a", one}})) - assert.False(t, Coins{{"a", one}}.IsAnyGT(Coins{{"a", two}})) - assert.True(t, Coins{{"a", two}}.IsAnyGT(Coins{{"a", one}})) - assert.True(t, Coins{{"a", one}, {"b", two}}.IsAnyGT(Coins{{"a", one}, {"b", one}})) - assert.True(t, Coins{{"a", two}, {"b", one}}.IsAnyGT(Coins{{"a", one}, {"b", two}})) -} - func TestCoinsIsAnyGTE(t *testing.T) { one := NewInt(1) two := NewInt(2) + assert.False(t, Coins{}.IsAnyGTE(Coins{})) assert.False(t, Coins{{"a", one}}.IsAnyGTE(Coins{})) assert.False(t, Coins{}.IsAnyGTE(Coins{{"a", one}})) assert.False(t, Coins{{"a", one}}.IsAnyGTE(Coins{{"a", two}})) + assert.True(t, Coins{{"a", one}, {"b", two}}.IsAnyGTE(Coins{{"a", two}, {"b", one}})) assert.True(t, Coins{{"a", one}}.IsAnyGTE(Coins{{"a", one}})) assert.True(t, Coins{{"a", two}}.IsAnyGTE(Coins{{"a", one}})) + assert.True(t, Coins{{"a", one}}.IsAnyGTE(Coins{{"a", one}, {"b", two}})) assert.True(t, Coins{{"a", one}, {"b", two}}.IsAnyGTE(Coins{{"a", one}, {"b", one}})) assert.True(t, Coins{{"a", one}, {"b", one}}.IsAnyGTE(Coins{{"a", one}, {"b", two}})) }