Skip to content

Commit

Permalink
fix: types: ensure .Amount is non-nil in Coin.Validate()
Browse files Browse the repository at this point in the history
This change fixes a scenario in which Coin.Validate() would
panic when given a nil Amount. While here, added a fuzz test
along with unit/regression tests.

Fixes #15690
  • Loading branch information
odeke-em committed Apr 4, 2023
1 parent 445dc8a commit 5217ed6
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

### Bug Fixes

* (types) [#15691](https://github.com/cosmos/cosmos-sdk/pull/15691) Make Coin.Validate() check that .Amount is not nil
* (types) [#15433](https://github.com/cosmos/cosmos-sdk/pull/15433) Allow disabling of account address caches (for printing bech32 account addresses).
* (x/auth) [#15059](https://github.com/cosmos/cosmos-sdk/pull/15059) `ante.CountSubKeys` returns 0 when passing a nil `Pubkey`.
* (x/capability) [#15030](https://github.com/cosmos/cosmos-sdk/pull/15030) Prevent `x/capability` from consuming `GasMeter` gas during `InitMemStore`
Expand Down
5 changes: 5 additions & 0 deletions types/coin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package types

import (
"encoding/json"
"errors"
"fmt"
"regexp"
"sort"
Expand Down Expand Up @@ -44,6 +45,10 @@ func (coin Coin) Validate() error {
return err
}

if coin.Amount.IsNil() {
return errors.New("amount is nil")
}

if coin.Amount.IsNegative() {
return fmt.Errorf("negative coin amount: %v", coin.Amount)
}
Expand Down
35 changes: 35 additions & 0 deletions types/coin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1257,6 +1257,7 @@ func (s *coinTestSuite) TestMarshalJSONCoins() {
}{
{"nil coins", nil, `[]`},
{"empty coins", sdk.Coins{}, `[]`},
{"empty coins", sdk.Coins{}, `[]`},
{"non-empty coins", sdk.NewCoins(sdk.NewInt64Coin("foo", 50)), `[{"denom":"foo","amount":"50"}]`},
}

Expand All @@ -1276,6 +1277,40 @@ func (s *coinTestSuite) TestMarshalJSONCoins() {
}
}

func (s *coinTestSuite) TestCoinValidate() {
testCases := []struct {
name string
coin sdk.Coin
wantErr string
}{
{"nil coin: nil Amount", sdk.Coin{}, "invalid denom"},
{"non-blank coin, nil Amount", sdk.Coin{Denom: "atom"}, "nil Amount"},
{"valid coin", sdk.Coin{Denom: "atom", Amount: math.NewInt(100)}, ""},
{"valid coin negative", sdk.Coin{Denom: "atom", Amount: math.NewInt(-999)}, ""},
}

for _, tc := range testCases {
tc := tc
t := s.T()
t.Run(tc.name, func(t *testing.T) {
err := tc.coin.Validate()
if tc.wantErr == "" {
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
return
} else {
if err == nil {
t.Error("Expected an error")
} else if !strings.Contains(err.Error(), tc.wantErr) {
t.Errorf("Error mismatch\n\tGot: %q\nWant: %q", err, tc.wantErr)
}
}
})
}

}

func (s *coinTestSuite) TestCoinAminoEncoding() {
cdc := codec.NewLegacyAmino()
c := sdk.NewInt64Coin(testDenom1, 5)
Expand Down
24 changes: 24 additions & 0 deletions types/fuzz_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package types

import (
"testing"

"github.com/cosmos/cosmos-sdk/codec"
)

func FuzzCoinUnmarshalJSON(f *testing.F) {
if testing.Short() {
f.Skip()
}

cdc := codec.NewLegacyAmino()
f.Add(`{"denom":"atom","amount":"1000"}`)
f.Add(`{"denom":"atom","amount":"-1000"}`)
f.Add(`{"denom":"uatom","amount":"1000111111111111111111111"}`)
f.Add(`{"denom":"mu","amount":"0"}`)

f.Fuzz(func(t *testing.T, jsonBlob string) {
var c Coin
_ = cdc.UnmarshalJSON([]byte(jsonBlob), &c)
})
}

0 comments on commit 5217ed6

Please sign in to comment.