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

Add feature uniqueness check on the FT and NFT asset issuance. #481

Merged
merged 4 commits into from
May 3, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions x/asset/ft/types/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ func (msg MsgIssue) ValidateBasic() error {
return sdkerrors.Wrapf(ErrInvalidInput, "invalid description %q, the length must be less than %d", msg.Description, maxDescriptionLength)
}

featuresSet := make(map[Feature]struct{})
for _, feature := range msg.Features {
if _, exists := featuresSet[feature]; exists {
return sdkerrors.Wrapf(ErrInvalidInput, "duplicated features in the features list")
}
featuresSet[feature] = struct{}{}
}

return nil
}

Expand Down
185 changes: 132 additions & 53 deletions x/asset/ft/types/msgs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,63 +23,142 @@ func TestMain(m *testing.M) {
m.Run()
}

//nolint:funlen // there are too many tests cases
func TestMsgIssue_ValidateBasic(t *testing.T) {
requireT := require.New(t)
acc := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())

msgF := func() types.MsgIssue {
return types.MsgIssue{
Issuer: acc.String(),
Symbol: "BTC",
Subunit: "btc",
Precision: 1,
Description: "BTC Description",
InitialAmount: sdk.NewInt(777),
}
validMessage := types.MsgIssue{
Issuer: acc.String(),
Symbol: "BTC",
Subunit: "btc",
Precision: 1,
Description: "BTC Description",
InitialAmount: sdk.NewInt(777),
}

msg := msgF()
requireT.NoError(msg.ValidateBasic())

msg = msgF()
msg.Issuer = "invalid"
requireT.Error(msg.ValidateBasic())

msg = msgF()
msg.Symbol = ""
requireT.Error(msg.ValidateBasic())

msg = msgF()
msg.Symbol = string(make([]byte, 10000))
requireT.Error(msg.ValidateBasic())

msg = msgF()
msg.Symbol = "1BT"
requireT.Error(msg.ValidateBasic())

msg = msgF()
msg.InitialAmount = sdk.Int{}
requireT.Error(msg.ValidateBasic())

msg = msgF()
msg.InitialAmount = sdk.NewInt(-100)
requireT.Error(msg.ValidateBasic())

msg = msgF()
msg.Description = string(make([]byte, 10000))
requireT.Error(msg.ValidateBasic())

msg = msgF()
msg.Subunit = ""
requireT.Error(msg.ValidateBasic())

msg = msgF()
msg.BurnRate = sdk.MustNewDecFromStr("-0.1")
requireT.Error(msg.ValidateBasic())

msg = msgF()
msg.SendCommissionRate = sdk.MustNewDecFromStr("-0.1")
requireT.Error(msg.ValidateBasic())
testCases := []struct {
name string
messageFunc func(types.MsgIssue) types.MsgIssue
expectedError error
}{
{
name: "valid",
messageFunc: func(msg types.MsgIssue) types.MsgIssue {
return msg
},
},
{
name: "invalid issuer address",
messageFunc: func(msg types.MsgIssue) types.MsgIssue {
msg.Issuer = "invalid"
return msg
},
expectedError: sdkerrors.ErrInvalidAddress,
},
{
name: "invalid missing symbol",
messageFunc: func(msg types.MsgIssue) types.MsgIssue {
msg.Symbol = ""
return msg
},
expectedError: types.ErrInvalidInput,
},
{
name: "invalid long symbol",
messageFunc: func(msg types.MsgIssue) types.MsgIssue {
msg.Symbol = string(make([]byte, 10000))
return msg
},
expectedError: types.ErrInvalidInput,
},
{
name: "invalid prohibited chars in symbol",
messageFunc: func(msg types.MsgIssue) types.MsgIssue {
msg.Symbol = "1BT"
return msg
},
expectedError: types.ErrInvalidInput,
},
{
name: "invalid nil initial amount",
messageFunc: func(msg types.MsgIssue) types.MsgIssue {
msg.InitialAmount = sdk.Int{} // nil
return msg
},
expectedError: types.ErrInvalidInput,
},
{
name: "invalid negative initial amount",
messageFunc: func(msg types.MsgIssue) types.MsgIssue {
msg.InitialAmount = sdk.NewInt(-100)
return msg
},
expectedError: types.ErrInvalidInput,
},
{
name: "invalid long description",
messageFunc: func(msg types.MsgIssue) types.MsgIssue {
msg.Description = string(make([]byte, 10000))
return msg
},
expectedError: types.ErrInvalidInput,
},
{
name: "invalid empty subunit",
messageFunc: func(msg types.MsgIssue) types.MsgIssue {
msg.Subunit = ""
return msg
},
expectedError: types.ErrInvalidInput,
},
{
name: "invalid empty subunit",
messageFunc: func(msg types.MsgIssue) types.MsgIssue {
msg.Subunit = ""
return msg
},
expectedError: types.ErrInvalidInput,
},
{
name: "invalid negative burn rate",
messageFunc: func(msg types.MsgIssue) types.MsgIssue {
msg.BurnRate = sdk.MustNewDecFromStr("-0.1")
return msg
},
expectedError: types.ErrInvalidInput,
},
{
name: "invalid negative send commission rate",
messageFunc: func(msg types.MsgIssue) types.MsgIssue {
msg.SendCommissionRate = sdk.MustNewDecFromStr("-0.1")
return msg
},
expectedError: types.ErrInvalidInput,
},
{
name: "invalid duplicated feature",
messageFunc: func(msg types.MsgIssue) types.MsgIssue {
msg.Features = []types.Feature{
types.Feature_burning,
types.Feature_whitelisting,
types.Feature_burning,
}
return msg
},
expectedError: types.ErrInvalidInput,
},
}
for _, testCase := range testCases {
tc := testCase
t.Run(tc.name, func(t *testing.T) {
assertT := assert.New(t)
err := tc.messageFunc(validMessage).ValidateBasic()
if tc.expectedError == nil {
assertT.NoError(err)
} else {
assertT.True(sdkerrors.IsOf(err, tc.expectedError))
}
})
}
}

func TestMsgFreeze_ValidateBasic(t *testing.T) {
Expand Down
8 changes: 8 additions & 0 deletions x/asset/nft/types/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ func (msg *MsgIssueClass) ValidateBasic() error {
return sdkerrors.Wrapf(ErrInvalidInput, "invalid URI hash %q, the length must be less than or equal %d", len(msg.URIHash), MaxURIHashLength)
}

featuresSet := make(map[ClassFeature]struct{})
for _, feature := range msg.Features {
if _, exists := featuresSet[feature]; exists {
return sdkerrors.Wrapf(ErrInvalidInput, "duplicated class features in the features list")
}
featuresSet[feature] = struct{}{}
}

return nil
}

Expand Down
13 changes: 13 additions & 0 deletions x/asset/nft/types/msgs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,19 @@ func TestMsgIssueClass_ValidateBasic(t *testing.T) {
},
expectedError: types.ErrInvalidInput,
},
{
name: "invalid duplicated class feature",
messageFunc: func() *types.MsgIssueClass {
msg := validMessage
msg.Features = []types.ClassFeature{
types.ClassFeature_burning,
types.ClassFeature_whitelisting,
types.ClassFeature_burning,
}
return &msg
},
expectedError: types.ErrInvalidInput,
},
}

for _, testCase := range testCases {
Expand Down
6 changes: 2 additions & 4 deletions x/deterministicgas/spec/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ import (

//go:generate go run . ./README.md

var (
//go:embed README.tmpl.md
readmeTmpl string
)
//go:embed README.tmpl.md
var readmeTmpl string

func main() {
type determMsg struct {
Expand Down