Skip to content

Commit

Permalink
feat: add Coins.Find method (#11959)
Browse files Browse the repository at this point in the history
Added a new helper method to find a coin in a set of `sdk.Coins` by denom.

---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [x] added `!` to the type prefix if API or client breaking change
- [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)

(cherry picked from commit 23baecf)

# Conflicts:
#	CHANGELOG.md
  • Loading branch information
robert-zaremba authored and mergify[bot] committed May 18, 2022
1 parent 162686b commit 49f8cb7
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 15 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ Ref: https://keepachangelog.com/en/1.0.0/
* [\#10962](https://github.com/cosmos/cosmos-sdk/pull/10962) ADR-040: Add state migration from iavl (v1Store) to smt (v2Store)
* (types) [\#10948](https://github.com/cosmos/cosmos-sdk/issues/10948) Add `app-db-backend` to the `app.toml` config to replace the compile-time `types.DBbackend` variable.
* (authz)[\#11060](https://github.com/cosmos/cosmos-sdk/pull/11060) Support grant with no expire time.
<<<<<<< HEAD
=======
* (rosetta) [\#11590](https://github.com/cosmos/cosmos-sdk/pull/11590) Add fee suggestion for rosetta and enable offline mode. Also force set events about Fees to Success to pass reconciliation test.
* (types) [\#11959](https://github.com/cosmos/cosmos-sdk/pull/11959) Added `sdk.Coins.Find` helper method to find a coin by denom.
>>>>>>> 23baecf22 (feat: add Coins.Find method (#11959))
### API Breaking Changes

Expand Down
23 changes: 16 additions & 7 deletions types/coin.go
Original file line number Diff line number Diff line change
Expand Up @@ -693,28 +693,37 @@ func (coins Coins) AmountOf(denom string) Int {
// AmountOfNoDenomValidation returns the amount of a denom from coins
// without validating the denomination.
func (coins Coins) AmountOfNoDenomValidation(denom string) Int {
if ok, c := coins.Find(denom); ok {
return c.Amount
}
return ZeroInt()
}

// Find returns true and coin if the denom exists in coins. Otherwise it returns false
// and a zero coin. Uses binary search.
// CONTRACT: coins must be valid (sorted).
func (coins Coins) Find(denom string) (bool, Coin) {
switch len(coins) {
case 0:
return ZeroInt()
return false, Coin{}

case 1:
coin := coins[0]
if coin.Denom == denom {
return coin.Amount
return true, coin
}
return ZeroInt()
return false, Coin{}

default:
// Binary search the amount of coins remaining
midIdx := len(coins) / 2 // 2:1, 3:1, 4:2
coin := coins[midIdx]
switch {
case denom < coin.Denom:
return coins[:midIdx].AmountOfNoDenomValidation(denom)
return coins[:midIdx].Find(denom)
case denom == coin.Denom:
return coin.Amount
return true, coin
default:
return coins[midIdx+1:].AmountOfNoDenomValidation(denom)
return coins[midIdx+1:].Find(denom)
}
}
}
Expand Down
42 changes: 34 additions & 8 deletions types/coin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,8 @@ func (s *coinTestSuite) TestSortCoins() {
}
}

func (s *coinTestSuite) TestAmountOf() {
func (s *coinTestSuite) TestSearch() {
require := s.Require()
case0 := sdk.Coins{}
case1 := sdk.Coins{
sdk.NewInt64Coin("gold", 0),
Expand All @@ -925,7 +926,7 @@ func (s *coinTestSuite) TestAmountOf() {
sdk.NewInt64Coin("gas", 8),
}

cases := []struct {
amountOfCases := []struct {
coins sdk.Coins
amountOf int64
amountOfSpace int64
Expand All @@ -940,13 +941,38 @@ func (s *coinTestSuite) TestAmountOf() {
{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.Run("AmountOf", func() {
for i, tc := range amountOfCases {
require.Equal(sdk.NewInt(tc.amountOfGAS), tc.coins.AmountOf("gas"), i)
require.Equal(sdk.NewInt(tc.amountOfMINERAL), tc.coins.AmountOf("mineral"), i)
require.Equal(sdk.NewInt(tc.amountOfTREE), tc.coins.AmountOf("tree"), i)
}
require.Panics(func() { amountOfCases[0].coins.AmountOf("10Invalid") })
})

s.Require().Panics(func() { cases[0].coins.AmountOf("10Invalid") })
zeroCoin := sdk.Coin{}
findCases := []struct {
coins sdk.Coins
denom string
expectedOk bool
expectedCoin sdk.Coin
}{
{case0, "any", false, zeroCoin},
{case1, "other", false, zeroCoin},
{case1, "gold", true, case1[0]},
{case4, "gas", true, case4[0]},
{case2, "gas", true, case2[0]},
{case2, "mineral", true, case2[1]},
{case2, "tree", true, case2[2]},
{case2, "other", false, zeroCoin},
}
s.Run("Find", func() {
for i, tc := range findCases {
ok, c := tc.coins.Find(tc.denom)
require.Equal(tc.expectedOk, ok, i)
require.Equal(tc.expectedCoin, c, i)
}
})
}

func (s *coinTestSuite) TestCoinsIsAnyGTE() {
Expand Down

0 comments on commit 49f8cb7

Please sign in to comment.