fix: types: ensure .Amount is non-nil in Coin.Validate() (#15691)

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
This commit is contained in:
Emmanuel T Odeke 2023-04-05 00:36:36 -07:00 committed by GitHub
parent 5b1ee227c2
commit a7e2df2a46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 64 additions and 0 deletions

View File

@ -168,6 +168,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`

View File

@ -2,6 +2,7 @@ package types
import (
"encoding/json"
"errors"
"fmt"
"regexp"
"sort"
@ -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)
}

View File

@ -1276,6 +1276,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"}, "amount is nil"},
{"valid coin", sdk.Coin{Denom: "atom", Amount: math.NewInt(100)}, ""},
{"negative coin", sdk.Coin{Denom: "atom", Amount: math.NewInt(-999)}, "negative coin amount"},
}
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)

24
types/fuzz_test.go Normal file
View File

@ -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)
})
}