Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

R4R: Add custom validation for denom #6755

Merged
merged 13 commits into from
Oct 6, 2020
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ be used to retrieve the actual proposal `Content`. Also the `NewMsgSubmitProposa

### Features

* [\#6755](https://github.com/cosmos/cosmos-sdk/pull/6755) Add custom regex validation for `Coin` denom by overwriting `CoinDenomRegex` when using `/types/coin.go`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@okwme this should be moved across the the Launchpad branch as 0.39.2 will be the first release where this new feature is introduced.

* [\#7265](https://github.com/cosmos/cosmos-sdk/pull/7265) Support Tendermint block pruning through a new `min-retain-blocks` configuration that can be set in either `app.toml` or via the CLI. This parameter is used in conjunction with other criteria to determine the height at which Tendermint should prune blocks.
* (vesting) [\#7209](https://github.com/cosmos/cosmos-sdk/pull/7209) Create new `MsgCreateVestingAccount` message type along with CLI handler that allows for the creation of delayed and continuous vesting types.
* (events) [\#7121](https://github.com/cosmos/cosmos-sdk/pull/7121) The application now drives what events are indexed by Tendermint via the `index-events` configuration in `app.toml`, which is a list of events taking the form `{eventType}.{attributeKey}`.
Expand Down
31 changes: 24 additions & 7 deletions types/coin.go
Original file line number Diff line number Diff line change
Expand Up @@ -602,15 +602,32 @@ var (
reAmt = `[[:digit:]]+`
reDecAmt = `[[:digit:]]*\.[[:digit:]]+`
reSpc = `[[:space:]]*`
reDnm = regexp.MustCompile(fmt.Sprintf(`^%s$`, reDnmString))
reCoin = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reAmt, reSpc, reDnmString))
reDecCoin = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reDecAmt, reSpc, reDnmString))
reDnm = returnReDnm
reCoin = returnReCoin
reDecCoin = returnDecCoin
)

// ValidateDenom validates a denomination string returning an error if it is
// invalid.
func returnDecCoin() *regexp.Regexp {
return regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reDecAmt, reSpc, CoinDenomRegex()))
}
func returnReCoin() *regexp.Regexp {
return regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reAmt, reSpc, CoinDenomRegex()))
}
func returnReDnm() *regexp.Regexp {
return regexp.MustCompile(fmt.Sprintf(`^%s$`, CoinDenomRegex()))
}

// DefaultCoinDenomRegex returns the default regex string
func DefaultCoinDenomRegex() string {
return reDnmString
}

// CoinDenomRegex returns the current regex string and can be overwritten for custom validation
var CoinDenomRegex = DefaultCoinDenomRegex

// ValidateDenom is the default validation function for Coin.Denom.
func ValidateDenom(denom string) error {
if !reDnm.MatchString(denom) {
if !reDnm().MatchString(denom) {
return fmt.Errorf("invalid denom: %s", denom)
}
return nil
Expand All @@ -628,7 +645,7 @@ func mustValidateDenom(denom string) {
func ParseCoin(coinStr string) (coin Coin, err error) {
coinStr = strings.TrimSpace(coinStr)

matches := reCoin.FindStringSubmatch(coinStr)
matches := reCoin().FindStringSubmatch(coinStr)
if matches == nil {
return Coin{}, fmt.Errorf("invalid coin expression: %s", coinStr)
}
Expand Down
24 changes: 24 additions & 0 deletions types/coin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,30 @@ func (s *coinTestSuite) TestCoinIsValid() {
}
}

func (s *coinTestSuite) TestCustomValidation() {

newDnmRegex := `[\x{1F600}-\x{1F6FF}]`
sdk.CoinDenomRegex = func() string {
return newDnmRegex
}

cases := []struct {
coin sdk.Coin
expectPass bool
}{
{sdk.Coin{"🙂", sdk.NewInt(1)}, true},
{sdk.Coin{"🙁", sdk.NewInt(1)}, true},
{sdk.Coin{"🌶", sdk.NewInt(1)}, false}, // outside the unicode range listed above
{sdk.Coin{"asdf", sdk.NewInt(1)}, false},
{sdk.Coin{"", sdk.NewInt(1)}, false},
}

for i, tc := range cases {
s.Require().Equal(tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i)
}
sdk.CoinDenomRegex = sdk.DefaultCoinDenomRegex
}

func (s *coinTestSuite) TestAddCoin() {
cases := []struct {
inputOne sdk.Coin
Expand Down
2 changes: 1 addition & 1 deletion types/dec_coin.go
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,7 @@ func (coins DecCoins) Sort() DecCoins {
func ParseDecCoin(coinStr string) (coin DecCoin, err error) {
coinStr = strings.TrimSpace(coinStr)

matches := reDecCoin.FindStringSubmatch(coinStr)
matches := reDecCoin().FindStringSubmatch(coinStr)
if matches == nil {
return DecCoin{}, fmt.Errorf("invalid decimal coin expression: %s", coinStr)
}
Expand Down