fix: Add SafeSub for sdk.Coin (#11630)

## Description

Closes: #11603



---

### 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...

- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/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/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/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)
This commit is contained in:
NagaTulasi 2022-04-15 00:45:57 +05:30 committed by GitHub
parent ce44b35cac
commit 6ff6dce275
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 43 additions and 10 deletions

View File

@ -175,6 +175,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Improvements
* [\#11630](https://github.com/cosmos/cosmos-sdk/pull/11630) Add SafeSub method to sdk.Coin.
* [\#11511](https://github.com/cosmos/cosmos-sdk/pull/11511) Add api server flags to start command.
* [\#11484](https://github.com/cosmos/cosmos-sdk/pull/11484) Implement getter for keyring backend option.
* [\#11449](https://github.com/cosmos/cosmos-sdk/pull/11449) Improved error messages when node isn't synced.

View File

@ -115,19 +115,29 @@ func (coin Coin) AddAmount(amount Int) Coin {
return Coin{coin.Denom, coin.Amount.Add(amount)}
}
// Sub subtracts amounts of two coins with same denom. If the coins differ in denom
// then it panics.
// Sub subtracts amounts of two coins with same denom and panics on error.
func (coin Coin) Sub(coinB Coin) Coin {
res, err := coin.SafeSub(coinB)
if err != nil {
panic(err)
}
return res
}
// SafeSub safely subtracts the amounts of two coins. It returns an error if the coins differ
// in denom or subtraction results in negative coin denom.
func (coin Coin) SafeSub(coinB Coin) (Coin, error) {
if coin.Denom != coinB.Denom {
panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, coinB.Denom))
return Coin{}, fmt.Errorf("invalid coin denoms: %s, %s", coin.Denom, coinB.Denom)
}
res := Coin{coin.Denom, coin.Amount.Sub(coinB.Amount)}
if res.IsNegative() {
panic("negative coin amount")
return Coin{}, fmt.Errorf("negative coin amount")
}
return res
return res, nil
}
// SubAmount subtracts an amount from the Coin.

View File

@ -505,6 +505,30 @@ func (s *coinTestSuite) TestSubCoins() {
}
}
func (s *coinTestSuite) TestSafeSubCoin() {
cases := []struct {
inputOne sdk.Coin
inputTwo sdk.Coin
expected sdk.Coin
expErrMsg string
}{
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), sdk.NewInt64Coin(testDenom1, 1), "invalid coin denoms"},
{sdk.NewInt64Coin(testDenom1, 10), sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 9), ""},
{sdk.NewInt64Coin(testDenom1, 5), sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom1, 5), ""},
{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 5), sdk.Coin{}, "negative coin amount"},
}
for _, tc := range cases {
tc := tc
res, err := tc.inputOne.SafeSub(tc.inputTwo)
if err != nil {
s.Require().Contains(err.Error(), tc.expErrMsg)
return
}
s.Require().Equal(tc.expected, res)
}
}
func (s *coinTestSuite) TestCoins_Validate() {
testCases := []struct {
name string

View File

@ -103,12 +103,10 @@ func (a StakeAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.AcceptRe
Updated: &StakeAuthorization{Validators: a.GetValidators(), AuthorizationType: a.GetAuthorizationType()}}, nil
}
// check sufficient balance exists.
if _, isNegative := sdk.NewCoins(*a.MaxTokens).SafeSub(sdk.NewCoins(amount)); isNegative {
return authz.AcceptResponse{}, sdkerrors.ErrInsufficientFunds.Wrapf("amount is more than max tokens")
limitLeft, err := a.MaxTokens.SafeSub(amount)
if err != nil {
return authz.AcceptResponse{}, err
}
limitLeft := a.MaxTokens.Sub(amount)
if limitLeft.IsZero() {
return authz.AcceptResponse{Accept: true, Delete: true}, nil
}