Skip to content

Commit 3e6089d

Browse files
authored
R4R: Add custom validation for denom (#6755)
Allow ValidateDenom to be customized per application. closes: #6744
1 parent 090bae5 commit 3e6089d

File tree

4 files changed

+50
-8
lines changed

4 files changed

+50
-8
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ be used to retrieve the actual proposal `Content`. Also the `NewMsgSubmitProposa
163163

164164
### Features
165165

166+
* [\#6755](https://github.com/cosmos/cosmos-sdk/pull/6755) Add custom regex validation for `Coin` denom by overwriting `CoinDenomRegex` when using `/types/coin.go`.
166167
* [\#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.
167168
* (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.
168169
* (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}`.

types/coin.go

+24-7
Original file line numberDiff line numberDiff line change
@@ -602,15 +602,32 @@ var (
602602
reAmt = `[[:digit:]]+`
603603
reDecAmt = `[[:digit:]]*\.[[:digit:]]+`
604604
reSpc = `[[:space:]]*`
605-
reDnm = regexp.MustCompile(fmt.Sprintf(`^%s$`, reDnmString))
606-
reCoin = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reAmt, reSpc, reDnmString))
607-
reDecCoin = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reDecAmt, reSpc, reDnmString))
605+
reDnm = returnReDnm
606+
reCoin = returnReCoin
607+
reDecCoin = returnDecCoin
608608
)
609609

610-
// ValidateDenom validates a denomination string returning an error if it is
611-
// invalid.
610+
func returnDecCoin() *regexp.Regexp {
611+
return regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reDecAmt, reSpc, CoinDenomRegex()))
612+
}
613+
func returnReCoin() *regexp.Regexp {
614+
return regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reAmt, reSpc, CoinDenomRegex()))
615+
}
616+
func returnReDnm() *regexp.Regexp {
617+
return regexp.MustCompile(fmt.Sprintf(`^%s$`, CoinDenomRegex()))
618+
}
619+
620+
// DefaultCoinDenomRegex returns the default regex string
621+
func DefaultCoinDenomRegex() string {
622+
return reDnmString
623+
}
624+
625+
// CoinDenomRegex returns the current regex string and can be overwritten for custom validation
626+
var CoinDenomRegex = DefaultCoinDenomRegex
627+
628+
// ValidateDenom is the default validation function for Coin.Denom.
612629
func ValidateDenom(denom string) error {
613-
if !reDnm.MatchString(denom) {
630+
if !reDnm().MatchString(denom) {
614631
return fmt.Errorf("invalid denom: %s", denom)
615632
}
616633
return nil
@@ -628,7 +645,7 @@ func mustValidateDenom(denom string) {
628645
func ParseCoin(coinStr string) (coin Coin, err error) {
629646
coinStr = strings.TrimSpace(coinStr)
630647

631-
matches := reCoin.FindStringSubmatch(coinStr)
648+
matches := reCoin().FindStringSubmatch(coinStr)
632649
if matches == nil {
633650
return Coin{}, fmt.Errorf("invalid coin expression: %s", coinStr)
634651
}

types/coin_test.go

+24
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,30 @@ func (s *coinTestSuite) TestCoinIsValid() {
9898
}
9999
}
100100

101+
func (s *coinTestSuite) TestCustomValidation() {
102+
103+
newDnmRegex := `[\x{1F600}-\x{1F6FF}]`
104+
sdk.CoinDenomRegex = func() string {
105+
return newDnmRegex
106+
}
107+
108+
cases := []struct {
109+
coin sdk.Coin
110+
expectPass bool
111+
}{
112+
{sdk.Coin{"🙂", sdk.NewInt(1)}, true},
113+
{sdk.Coin{"🙁", sdk.NewInt(1)}, true},
114+
{sdk.Coin{"🌶", sdk.NewInt(1)}, false}, // outside the unicode range listed above
115+
{sdk.Coin{"asdf", sdk.NewInt(1)}, false},
116+
{sdk.Coin{"", sdk.NewInt(1)}, false},
117+
}
118+
119+
for i, tc := range cases {
120+
s.Require().Equal(tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i)
121+
}
122+
sdk.CoinDenomRegex = sdk.DefaultCoinDenomRegex
123+
}
124+
101125
func (s *coinTestSuite) TestAddCoin() {
102126
cases := []struct {
103127
inputOne sdk.Coin

types/dec_coin.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@ func (coins DecCoins) Sort() DecCoins {
615615
func ParseDecCoin(coinStr string) (coin DecCoin, err error) {
616616
coinStr = strings.TrimSpace(coinStr)
617617

618-
matches := reDecCoin.FindStringSubmatch(coinStr)
618+
matches := reDecCoin().FindStringSubmatch(coinStr)
619619
if matches == nil {
620620
return DecCoin{}, fmt.Errorf("invalid decimal coin expression: %s", coinStr)
621621
}

0 commit comments

Comments
 (0)