From 66d5e4adc2d1ff05d98ac19e60056a5bb0ed68cc Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 12 Oct 2021 12:47:54 +0200 Subject: [PATCH] fix: null guard for tx fee amounts (backport #10327) (#10342) * fix: null guard for tx fee amounts (#10327) ## Description It is possible to submit a TX with a fees object containing a Coin with a nil amount. This results in a rather cryptic redacted panic response when the basic validation checks fee Coins for negative amounts. This PR adds an additional check for nil to provide a friendlier error message. --- ### 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 - [ ] 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/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification (note: No issue exists) - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) (note: First PR against the SDK so please comment with what needs to be done) - [x] added a changelog entry to `CHANGELOG.md` - [x] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [x] 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 96e162b8a3939c7999c0c12c39d196698594306c) # Conflicts: # CHANGELOG.md * solve conflicts * move sections Co-authored-by: Alex Megalokonomos Co-authored-by: marbar3778 --- CHANGELOG.md | 1 + types/coin.go | 18 ++++++++++++++++++ types/coin_test.go | 27 +++++++++++++++++++++++++++ types/tx/types.go | 7 +++++++ 4 files changed, 53 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 196e70fb806e..c1509b1e44e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements * [\#10262](https://github.com/cosmos/cosmos-sdk/pull/10262) Remove unnecessary logging in `x/feegrant` simulation. +* [\#10327](https://github.com/cosmos/cosmos-sdk/pull/10327) Add null guard for possible nil `Amount` in tx fee `Coins` ### Bug Fixes diff --git a/types/coin.go b/types/coin.go index e8fe7a5f8781..185f8b28e4c4 100644 --- a/types/coin.go +++ b/types/coin.go @@ -144,6 +144,11 @@ func (coin Coin) IsNegative() bool { return coin.Amount.Sign() == -1 } +// IsNil returns true if the coin amount is nil and false otherwise. +func (coin Coin) IsNil() bool { + return coin.Amount.i == nil +} + //----------------------------------------------------------------------------- // Coins @@ -585,6 +590,19 @@ func (coins Coins) IsAnyNegative() bool { return false } +// IsAnyNil returns true if there is at least one coin whose amount +// is nil; returns false otherwise. It returns false if the coin set +// is empty too. +func (coins Coins) IsAnyNil() bool { + for _, coin := range coins { + if coin.IsNil() { + return true + } + } + + return false +} + // negative returns a set of coins with all amount negative. // // TODO: Remove once unsigned integers are used. diff --git a/types/coin_test.go b/types/coin_test.go index 9a5d83fea9c3..240b15d11de0 100644 --- a/types/coin_test.go +++ b/types/coin_test.go @@ -282,6 +282,20 @@ func (s *coinTestSuite) TestCoinIsZero() { 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 @@ -944,6 +958,19 @@ func (s *coinTestSuite) TestCoinsIsAnyGT() { } } +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) diff --git a/types/tx/types.go b/types/tx/types.go index 84ce81edcbf8..3aa8bbbb5e55 100644 --- a/types/tx/types.go +++ b/types/tx/types.go @@ -62,6 +62,13 @@ func (t *Tx) ValidateBasic() error { ) } + if fee.Amount.IsAnyNil() { + return sdkerrors.Wrapf( + sdkerrors.ErrInsufficientFee, + "invalid fee provided: null", + ) + } + if fee.Amount.IsAnyNegative() { return sdkerrors.Wrapf( sdkerrors.ErrInsufficientFee,