From eb53ff0c71cf2910a3ed7264d9455d8e4a47d568 Mon Sep 17 00:00:00 2001 From: Jayden Lee <41176085+tkxkd0159@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:19:18 +0900 Subject: [PATCH] remove legacy address converter & ValidateBasic --- scripts/mockgen.sh | 27 - tests/e2e/collection/grpc.go | 6 +- tests/e2e/collection/suite.go | 12 +- tests/e2e/collection/tx.go | 14 +- x/collection/client/cli/query.go | 37 +- x/collection/client/cli/tx.go | 139 ++-- x/collection/client/cli/tx_test.go | 36 +- x/collection/collection.go | 4 +- x/collection/genesis.go | 19 +- x/collection/genesis_test.go | 4 +- x/collection/keeper/genesis.go | 11 +- x/collection/keeper/genesis_test.go | 2 +- x/collection/keeper/grpc_query.go | 2 +- x/collection/keeper/keeper_test.go | 30 +- x/collection/keeper/msg_server.go | 380 ++++++++++- x/collection/keeper/msg_server_test.go | 653 ++++++++++++++++--- x/collection/keeper/send_test.go | 10 +- x/collection/module/module.go | 19 +- x/collection/msgs.go | 357 +---------- x/collection/msgs_test.go | 844 ------------------------- 20 files changed, 1102 insertions(+), 1504 deletions(-) diff --git a/scripts/mockgen.sh b/scripts/mockgen.sh index 0fb8a30a66..1f433dffae 100755 --- a/scripts/mockgen.sh +++ b/scripts/mockgen.sh @@ -1,32 +1,5 @@ #!/usr/bin/env bash mockgen_cmd="mockgen" -$mockgen_cmd -source=baseapp/abci_utils.go -package mock -destination baseapp/testutil/mock/mocks.go -$mockgen_cmd -source=client/account_retriever.go -package mock -destination testutil/mock/account_retriever.go -$mockgen_cmd -package mock -destination store/mock/cosmos_cosmos_db_DB.go github.com/cosmos/cosmos-db DB -$mockgen_cmd -source=types/module/module.go -package mock -destination testutil/mock/types_module_module.go -$mockgen_cmd -source=types/module/mock_appmodule_test.go -package mock -destination testutil/mock/types_mock_appmodule.go -$mockgen_cmd -source=types/invariant.go -package mock -destination testutil/mock/types_invariant.go -$mockgen_cmd -package mock -destination testutil/mock/grpc_server.go github.com/cosmos/gogoproto/grpc Server -$mockgen_cmd -package mock -destination testutil/mock/logger.go cosmossdk.io/log Logger -$mockgen_cmd -source=orm/model/ormtable/hooks.go -package ormmocks -destination orm/testing/ormmocks/hooks.go -$mockgen_cmd -source=x/nft/expected_keepers.go -package testutil -destination x/nft/testutil/expected_keepers_mocks.go -$mockgen_cmd -source=x/feegrant/expected_keepers.go -package testutil -destination x/feegrant/testutil/expected_keepers_mocks.go -$mockgen_cmd -source=x/mint/types/expected_keepers.go -package testutil -destination x/mint/testutil/expected_keepers_mocks.go -$mockgen_cmd -source=x/params/proposal_handler_test.go -package testutil -destination x/params/testutil/staking_keeper_mock.go -$mockgen_cmd -source=x/crisis/types/expected_keepers.go -package testutil -destination x/crisis/testutil/expected_keepers_mocks.go -$mockgen_cmd -source=x/auth/tx/config/expected_keepers.go -package testutil -destination x/auth/tx/testutil/expected_keepers_mocks.go -$mockgen_cmd -source=x/auth/types/expected_keepers.go -package testutil -destination x/auth/testutil/expected_keepers_mocks.go -$mockgen_cmd -source=x/auth/ante/expected_keepers.go -package testutil -destination x/auth/ante/testutil/expected_keepers_mocks.go -$mockgen_cmd -source=x/authz/expected_keepers.go -package testutil -destination x/authz/testutil/expected_keepers_mocks.go -$mockgen_cmd -source=x/bank/types/expected_keepers.go -package testutil -destination x/bank/testutil/expected_keepers_mocks.go -$mockgen_cmd -source=x/group/testutil/expected_keepers.go -package testutil -destination x/group/testutil/expected_keepers_mocks.go -$mockgen_cmd -source=x/evidence/types/expected_keepers.go -package testutil -destination x/evidence/testutil/expected_keepers_mocks.go -$mockgen_cmd -source=x/distribution/types/expected_keepers.go -package testutil -destination x/distribution/testutil/expected_keepers_mocks.go -$mockgen_cmd -source=x/slashing/types/expected_keepers.go -package testutil -destination x/slashing/testutil/expected_keepers_mocks.go -$mockgen_cmd -source=x/genutil/types/expected_keepers.go -package testutil -destination x/genutil/testutil/expected_keepers_mocks.go -$mockgen_cmd -source=x/gov/testutil/expected_keepers.go -package testutil -destination x/gov/testutil/expected_keepers_mocks.go -$mockgen_cmd -source=x/staking/types/expected_keepers.go -package testutil -destination x/staking/testutil/expected_keepers_mocks.go -$mockgen_cmd -source=x/auth/vesting/types/expected_keepers.go -package testutil -destination x/auth/vesting/testutil/expected_keepers_mocks.go $mockgen_cmd -source=x/foundation/expected_keepers.go -package testutil -destination x/foundation/testutil/expected_keepers_mocks.go $mockgen_cmd -source=x/stakingplus/expected_keepers.go -package testutil -destination x/stakingplus/testutil/expected_keepers_mocks.go diff --git a/tests/e2e/collection/grpc.go b/tests/e2e/collection/grpc.go index 665dd96cd3..61067b949d 100644 --- a/tests/e2e/collection/grpc.go +++ b/tests/e2e/collection/grpc.go @@ -188,13 +188,13 @@ func (s *E2ETestSuite) TestStatisticsGRPC() { }, { "invalid request (wrong contract ID) - Minted", - fmt.Sprintf("%s/lbm/collection/v1/contracts/%s/token_types/%s/supply", val.APIAddress, "wrong ID", s.nftClassID), + fmt.Sprintf("%s/lbm/collection/v1/contracts/%s/token_types/%s/minted", val.APIAddress, "wrong ID", s.nftClassID), true, &collection.QueryNFTMintedResponse{}, }, { "invalid request (wrong contract ID) - Burnt", - fmt.Sprintf("%s/lbm/collection/v1/contracts/%s/token_types/%s/supply", val.APIAddress, "wrong ID", s.nftClassID), + fmt.Sprintf("%s/lbm/collection/v1/contracts/%s/token_types/%s/burnt", val.APIAddress, "wrong ID", s.nftClassID), true, &collection.QueryNFTBurntResponse{}, }, @@ -487,6 +487,8 @@ func (s *E2ETestSuite) TestGranteeGrantsGRPC() { val := s.network.Validators[0] s.grant(s.contractID, s.vendor, s.stranger, collection.PermissionIssue) dummyAddr := "link1hcpqj6w2eq30jcdggs7892lmask0cacvynqg7d" + _, err := s.ac.StringToBytes(dummyAddr) + s.Require().NoError(err) testCases := []struct { name string diff --git a/tests/e2e/collection/suite.go b/tests/e2e/collection/suite.go index 596a0ed778..207d2698d9 100644 --- a/tests/e2e/collection/suite.go +++ b/tests/e2e/collection/suite.go @@ -7,10 +7,10 @@ import ( "github.com/cosmos/gogoproto/proto" "github.com/stretchr/testify/suite" + "cosmossdk.io/core/address" cmath "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/testutil" @@ -26,6 +26,7 @@ type E2ETestSuite struct { suite.Suite cfg network.Config + ac address.Codec network *network.Network setupHeight int64 @@ -54,6 +55,7 @@ func (s *E2ETestSuite) SetupSuite() { var err error s.network, err = network.New(s.T(), s.T().TempDir(), s.cfg) s.Require().NoError(err) + s.ac = s.network.Config.InterfaceRegistry.SigningContext().AddressCodec() s.commonArgs = []string{ fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), @@ -144,7 +146,7 @@ func (s *E2ETestSuite) mintNFT(contractID string, operator, to sdk.AccAddress, c fmt.Sprintf("--%s=%s", cli.FlagName, "arctic fox"), }, s.commonArgs...) - out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdMintNFT(), args) + out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdMintNFT(s.ac), args) s.Require().NoError(err) txResp := s.getTxResp(out, 0) var event collection.EventMintedNFT @@ -185,7 +187,7 @@ func (s *E2ETestSuite) grant(contractID string, granter, grantee sdk.AccAddress, collection.LegacyPermission(permission).String(), }, s.commonArgs...) - out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdGrantPermission(), args) + out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdGrantPermission(s.ac), args) s.Require().NoError(err) _ = s.getTxResp(out, 0) } @@ -198,7 +200,7 @@ func (s *E2ETestSuite) authorizeOperator(contractID string, holder, operator sdk operator.String(), }, s.commonArgs...) - out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdAuthorizeOperator(), args) + out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdAuthorizeOperator(s.ac), args) s.Require().NoError(err) _ = s.getTxResp(out, 0) } @@ -225,7 +227,7 @@ func (s *E2ETestSuite) createAccount(uid string) sdk.AccAddress { addr, err := keyInfo.GetAddress() s.Require().NoError(err) - out, err := clitestutil.MsgSendExec(val.ClientCtx, val.Address, addr, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, cmath.NewInt(1000000))), address.NewBech32Codec("link"), s.commonArgs...) + out, err := clitestutil.MsgSendExec(val.ClientCtx, val.Address, addr, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, cmath.NewInt(1000000))), s.ac, s.commonArgs...) s.Require().NoError(err) s.getTxResp(out, 0) return addr diff --git a/tests/e2e/collection/tx.go b/tests/e2e/collection/tx.go index 18ec172999..63314030cb 100644 --- a/tests/e2e/collection/tx.go +++ b/tests/e2e/collection/tx.go @@ -59,7 +59,7 @@ func (s *E2ETestSuite) TestNewTxCmdSendNFT() { tc := tc s.Run(name, func() { - cmd := cli.NewTxCmdSendNFT() + cmd := cli.NewTxCmdSendNFT(s.ac) out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, append(tc.args, s.commonArgs...)) if !tc.valid { s.Require().Error(err) @@ -141,7 +141,7 @@ func (s *E2ETestSuite) TestNewTxCmdOperatorSendNFT() { tc := tc s.Run(name, func() { - cmd := cli.NewTxCmdOperatorSendNFT() + cmd := cli.NewTxCmdOperatorSendNFT(s.ac) out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, append(tc.args, s.commonArgs...)) if !tc.valid { s.Require().Error(err) @@ -308,7 +308,7 @@ func (s *E2ETestSuite) TestNewTxCmdMintNFT() { tc := tc s.Run(name, func() { - cmd := cli.NewTxCmdMintNFT() + cmd := cli.NewTxCmdMintNFT(s.ac) out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, append(tc.args, s.commonArgs...)) if !tc.valid { s.Require().Error(err) @@ -443,7 +443,7 @@ func (s *E2ETestSuite) TestNewTxCmdOperatorOperatorBurnNFT() { tc := tc s.Run(name, func() { - cmd := cli.NewTxCmdOperatorBurnNFT() + cmd := cli.NewTxCmdOperatorBurnNFT(s.ac) out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, append(tc.args, s.commonArgs...)) if !tc.valid { s.Require().Error(err) @@ -568,7 +568,7 @@ func (s *E2ETestSuite) TestNewTxCmdGrantPermission() { tc := tc s.Run(name, func() { - cmd := cli.NewTxCmdGrantPermission() + cmd := cli.NewTxCmdGrantPermission(s.ac) out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, append(tc.args, s.commonArgs...)) if !tc.valid { s.Require().Error(err) @@ -666,7 +666,7 @@ func (s *E2ETestSuite) TestNewTxCmdAuthorizeOperator() { tc := tc s.Run(name, func() { - cmd := cli.NewTxCmdAuthorizeOperator() + cmd := cli.NewTxCmdAuthorizeOperator(s.ac) out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, append(tc.args, s.commonArgs...)) if !tc.valid { s.Require().Error(err) @@ -715,7 +715,7 @@ func (s *E2ETestSuite) TestNewTxCmdRevokeOperator() { tc := tc s.Run(name, func() { - cmd := cli.NewTxCmdRevokeOperator() + cmd := cli.NewTxCmdRevokeOperator(s.ac) out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, append(tc.args, s.commonArgs...)) if !tc.valid { s.Require().Error(err) diff --git a/x/collection/client/cli/query.go b/x/collection/client/cli/query.go index b5a309c075..8f00e7a049 100644 --- a/x/collection/client/cli/query.go +++ b/x/collection/client/cli/query.go @@ -5,9 +5,10 @@ import ( "github.com/spf13/cobra" + "cosmossdk.io/core/address" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" "github.com/Finschia/finschia-sdk/x/collection" @@ -18,7 +19,7 @@ const ( ) // NewQueryCmd returns the cli query commands for this module -func NewQueryCmd() *cobra.Command { +func NewQueryCmd(ac address.Codec) *cobra.Command { queryCmd := &cobra.Command{ Use: collection.ModuleName, Short: fmt.Sprintf("Querying commands for the %s module", collection.ModuleName), @@ -29,22 +30,22 @@ func NewQueryCmd() *cobra.Command { } queryCmd.AddCommand( - NewQueryCmdBalances(), + NewQueryCmdBalances(ac), NewQueryCmdNFTSupply(), NewQueryCmdNFTMinted(), NewQueryCmdNFTBurnt(), NewQueryCmdContract(), NewQueryCmdToken(), NewQueryCmdTokenType(), - NewQueryCmdGranteeGrants(), - NewQueryCmdIsOperatorFor(), - NewQueryCmdHoldersByOperator(), + NewQueryCmdGranteeGrants(ac), + NewQueryCmdIsOperatorFor(ac), + NewQueryCmdHoldersByOperator(ac), ) return queryCmd } -func NewQueryCmdBalances() *cobra.Command { +func NewQueryCmdBalances(ac address.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "balances [contract-id] [address]", Args: cobra.ExactArgs(2), @@ -61,8 +62,8 @@ func NewQueryCmdBalances() *cobra.Command { return err } - address := args[1] - if _, err := sdk.AccAddressFromBech32(address); err != nil { + addr := args[1] + if _, err := ac.StringToBytes(addr); err != nil { return err } @@ -80,7 +81,7 @@ func NewQueryCmdBalances() *cobra.Command { req := &collection.QueryAllBalancesRequest{ ContractId: contractID, - Address: address, + Address: addr, Pagination: pageReq, } res, err := queryClient.AllBalances(cmd.Context(), req) @@ -96,7 +97,7 @@ func NewQueryCmdBalances() *cobra.Command { req := &collection.QueryBalanceRequest{ ContractId: contractID, - Address: address, + Address: addr, TokenId: tokenID, } res, err := queryClient.Balance(cmd.Context(), req) @@ -342,7 +343,7 @@ func NewQueryCmdToken() *cobra.Command { return cmd } -func NewQueryCmdGranteeGrants() *cobra.Command { +func NewQueryCmdGranteeGrants(ac address.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "grantee-grants [contract-id] [grantee]", Args: cobra.ExactArgs(2), @@ -360,7 +361,7 @@ func NewQueryCmdGranteeGrants() *cobra.Command { } grantee := args[1] - if _, err := sdk.AccAddressFromBech32(grantee); err != nil { + if _, err := ac.StringToBytes(grantee); err != nil { return err } @@ -381,7 +382,7 @@ func NewQueryCmdGranteeGrants() *cobra.Command { return cmd } -func NewQueryCmdIsOperatorFor() *cobra.Command { +func NewQueryCmdIsOperatorFor(ac address.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "approved [contract-id] [operator] [holder]", Args: cobra.ExactArgs(3), @@ -399,12 +400,12 @@ func NewQueryCmdIsOperatorFor() *cobra.Command { } operator := args[1] - if _, err := sdk.AccAddressFromBech32(operator); err != nil { + if _, err := ac.StringToBytes(operator); err != nil { return err } holder := args[2] - if _, err := sdk.AccAddressFromBech32(holder); err != nil { + if _, err := ac.StringToBytes(holder); err != nil { return err } @@ -426,7 +427,7 @@ func NewQueryCmdIsOperatorFor() *cobra.Command { return cmd } -func NewQueryCmdHoldersByOperator() *cobra.Command { +func NewQueryCmdHoldersByOperator(ac address.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "approvers [contract-id] [operator]", Args: cobra.ExactArgs(2), @@ -444,7 +445,7 @@ func NewQueryCmdHoldersByOperator() *cobra.Command { } operator := args[1] - if _, err := sdk.AccAddressFromBech32(operator); err != nil { + if _, err := ac.StringToBytes(operator); err != nil { return err } diff --git a/x/collection/client/cli/tx.go b/x/collection/client/cli/tx.go index 7a3c0a3dc3..4556b27e1c 100644 --- a/x/collection/client/cli/tx.go +++ b/x/collection/client/cli/tx.go @@ -6,6 +6,8 @@ import ( "github.com/spf13/cobra" + "cosmossdk.io/core/address" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" @@ -24,7 +26,7 @@ const ( ) // NewTxCmd returns the transaction commands for this module -func NewTxCmd() *cobra.Command { +func NewTxCmd(ac address.Codec) *cobra.Command { txCmd := &cobra.Command{ Use: collection.ModuleName, Short: fmt.Sprintf("%s transactions subcommands", collection.ModuleName), @@ -34,22 +36,22 @@ func NewTxCmd() *cobra.Command { } txCmd.AddCommand( - NewTxCmdSendNFT(), - NewTxCmdOperatorSendNFT(), + NewTxCmdSendNFT(ac), + NewTxCmdOperatorSendNFT(ac), NewTxCmdCreateContract(), NewTxCmdIssueNFT(), - NewTxCmdMintNFT(), - NewTxCmdGrantPermission(), + NewTxCmdMintNFT(ac), + NewTxCmdGrantPermission(ac), NewTxCmdRevokePermission(), - NewTxCmdAuthorizeOperator(), - NewTxCmdRevokeOperator(), + NewTxCmdAuthorizeOperator(ac), + NewTxCmdRevokeOperator(ac), NewTxCmdModify(), ) return txCmd } -func NewTxCmdSendNFT() *cobra.Command { +func NewTxCmdSendNFT(ac address.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "send-nft [contract-id] [from] [to] [token-id]", Args: cobra.ExactArgs(4), @@ -62,11 +64,17 @@ func NewTxCmdSendNFT() *cobra.Command { if err := cmd.Flags().Set(flags.FlagFrom, from); err != nil { return err } - clientCtx, err := client.GetClientTxContext(cmd) if err != nil { return err } + if err = collection.ValidateContractID(args[0]); err != nil { + return err + } + _, err = ac.StringToBytes(args[1]) + if err != nil { + return err + } msg := &collection.MsgSendNFT{ ContractId: args[0], @@ -74,9 +82,6 @@ func NewTxCmdSendNFT() *cobra.Command { To: args[2], TokenIds: []string{args[3]}, } - if err := msg.ValidateBasic(); err != nil { - return err - } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } @@ -85,13 +90,13 @@ func NewTxCmdSendNFT() *cobra.Command { return cmd } -func NewTxCmdOperatorSendNFT() *cobra.Command { +func NewTxCmdOperatorSendNFT(ac address.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: "operator-send-nft [contract-id] [operator] [from] [to] [amount]", + Use: "operator-send-nft [contract-id] [operator] [from] [to] [token-id]", Args: cobra.ExactArgs(5), Short: "send tokens by operator", Long: strings.TrimSpace(fmt.Sprintf(` - $ %s tx %s operator-send-nft [contract-id] [operator] [from] [to] [amount]`, version.AppName, collection.ModuleName), + $ %s tx %s operator-send-nft [contract-id] [operator] [from] [to] [token-id]`, version.AppName, collection.ModuleName), ), RunE: func(cmd *cobra.Command, args []string) error { operator := args[1] @@ -103,6 +108,17 @@ func NewTxCmdOperatorSendNFT() *cobra.Command { if err != nil { return err } + if err = collection.ValidateContractID(args[0]); err != nil { + return err + } + _, err = ac.StringToBytes(args[2]) + if err != nil { + return err + } + _, err = ac.StringToBytes(args[3]) + if err != nil { + return err + } msg := collection.MsgOperatorSendNFT{ ContractId: args[0], @@ -111,9 +127,6 @@ func NewTxCmdOperatorSendNFT() *cobra.Command { To: args[3], TokenIds: []string{args[4]}, } - if err := msg.ValidateBasic(); err != nil { - return err - } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) }, } @@ -162,9 +175,6 @@ func NewTxCmdCreateContract() *cobra.Command { Uri: baseImgURI, Meta: meta, } - if err := msg.ValidateBasic(); err != nil { - return err - } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) }, } @@ -196,6 +206,10 @@ func NewTxCmdIssueNFT() *cobra.Command { return err } + if err = collection.ValidateContractID(args[0]); err != nil { + return err + } + name, err := cmd.Flags().GetString(FlagName) if err != nil { return err @@ -212,9 +226,6 @@ func NewTxCmdIssueNFT() *cobra.Command { Name: name, Meta: meta, } - if err := msg.ValidateBasic(); err != nil { - return err - } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) }, } @@ -226,7 +237,7 @@ func NewTxCmdIssueNFT() *cobra.Command { return cmd } -func NewTxCmdMintNFT() *cobra.Command { +func NewTxCmdMintNFT(ac address.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "mint-nft [contract-id] [operator] [to] [class-id]", Args: cobra.ExactArgs(4), @@ -245,6 +256,10 @@ func NewTxCmdMintNFT() *cobra.Command { return err } + if err = collection.ValidateContractID(args[0]); err != nil { + return err + } + name, err := cmd.Flags().GetString(FlagName) if err != nil { return err @@ -255,6 +270,11 @@ func NewTxCmdMintNFT() *cobra.Command { return err } + _, err = ac.StringToBytes(args[2]) + if err != nil { + return err + } + params := []collection.MintNFTParam{{ TokenType: args[3], Name: name, @@ -267,9 +287,6 @@ func NewTxCmdMintNFT() *cobra.Command { To: args[2], Params: params, } - if err := msg.ValidateBasic(); err != nil { - return err - } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) }, } @@ -300,15 +317,15 @@ func NewTxCmdBurnNFT() *cobra.Command { if err != nil { return err } + if err = collection.ValidateContractID(args[0]); err != nil { + return err + } msg := collection.MsgBurnNFT{ ContractId: args[0], From: from, TokenIds: []string{args[2]}, } - if err := msg.ValidateBasic(); err != nil { - return err - } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) }, } @@ -317,7 +334,7 @@ func NewTxCmdBurnNFT() *cobra.Command { return cmd } -func NewTxCmdOperatorBurnNFT() *cobra.Command { +func NewTxCmdOperatorBurnNFT(ac address.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "operator-burn-nft [contract-id] [operator] [from] [token-id]", Args: cobra.ExactArgs(4), @@ -335,6 +352,13 @@ func NewTxCmdOperatorBurnNFT() *cobra.Command { if err != nil { return err } + if err = collection.ValidateContractID(args[0]); err != nil { + return err + } + _, err = ac.StringToBytes(args[2]) + if err != nil { + return err + } msg := collection.MsgOperatorBurnNFT{ ContractId: args[0], @@ -342,9 +366,6 @@ func NewTxCmdOperatorBurnNFT() *cobra.Command { From: args[2], TokenIds: []string{args[3]}, } - if err := msg.ValidateBasic(); err != nil { - return err - } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) }, } @@ -371,6 +392,9 @@ func NewTxCmdModify() *cobra.Command { if err != nil { return err } + if err = collection.ValidateContractID(args[0]); err != nil { + return err + } changes := []collection.Attribute{{ Key: args[4], @@ -383,9 +407,6 @@ func NewTxCmdModify() *cobra.Command { TokenIndex: args[3], Changes: changes, } - if err := msg.ValidateBasic(); err != nil { - return err - } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) }, } @@ -394,7 +415,7 @@ func NewTxCmdModify() *cobra.Command { return cmd } -func NewTxCmdGrantPermission() *cobra.Command { +func NewTxCmdGrantPermission(ac address.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "grant-permission [contract-id] [granter] [grantee] [permission]", Args: cobra.ExactArgs(4), @@ -412,6 +433,13 @@ func NewTxCmdGrantPermission() *cobra.Command { if err != nil { return err } + if err = collection.ValidateContractID(args[0]); err != nil { + return err + } + _, err = ac.StringToBytes(args[2]) + if err != nil { + return err + } msg := collection.MsgGrantPermission{ ContractId: args[0], @@ -419,9 +447,6 @@ func NewTxCmdGrantPermission() *cobra.Command { To: args[2], Permission: args[3], } - if err := msg.ValidateBasic(); err != nil { - return err - } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) }, } @@ -448,15 +473,15 @@ func NewTxCmdRevokePermission() *cobra.Command { if err != nil { return err } + if err = collection.ValidateContractID(args[0]); err != nil { + return err + } msg := collection.MsgRevokePermission{ ContractId: args[0], From: grantee, Permission: args[2], } - if err := msg.ValidateBasic(); err != nil { - return err - } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) }, } @@ -465,7 +490,7 @@ func NewTxCmdRevokePermission() *cobra.Command { return cmd } -func NewTxCmdAuthorizeOperator() *cobra.Command { +func NewTxCmdAuthorizeOperator(ac address.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "authorize-operator [contract-id] [holder] [operator]", Args: cobra.ExactArgs(3), @@ -483,15 +508,19 @@ func NewTxCmdAuthorizeOperator() *cobra.Command { if err != nil { return err } + if err = collection.ValidateContractID(args[0]); err != nil { + return err + } + _, err = ac.StringToBytes(args[2]) + if err != nil { + return err + } msg := collection.MsgAuthorizeOperator{ ContractId: args[0], Holder: holder, Operator: args[2], } - if err := msg.ValidateBasic(); err != nil { - return err - } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) }, } @@ -500,7 +529,7 @@ func NewTxCmdAuthorizeOperator() *cobra.Command { return cmd } -func NewTxCmdRevokeOperator() *cobra.Command { +func NewTxCmdRevokeOperator(ac address.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "revoke-operator [contract-id] [holder] [operator]", Args: cobra.ExactArgs(3), @@ -518,15 +547,19 @@ func NewTxCmdRevokeOperator() *cobra.Command { if err != nil { return err } + if err = collection.ValidateContractID(args[0]); err != nil { + return err + } + _, err = ac.StringToBytes(args[2]) + if err != nil { + return err + } msg := collection.MsgRevokeOperator{ ContractId: args[0], Holder: holder, Operator: args[2], } - if err := msg.ValidateBasic(); err != nil { - return err - } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) }, } diff --git a/x/collection/client/cli/tx_test.go b/x/collection/client/cli/tx_test.go index 80b3f72394..7da796f888 100644 --- a/x/collection/client/cli/tx_test.go +++ b/x/collection/client/cli/tx_test.go @@ -9,6 +9,7 @@ import ( rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" "github.com/stretchr/testify/suite" + "cosmossdk.io/core/address" sdkmath "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/client" @@ -29,6 +30,7 @@ type CLITestSuite struct { kr keyring.Keyring encCfg testutilmod.TestEncodingConfig + ac address.Codec baseCtx client.Context clientCtx client.Context commonFlags []string @@ -49,6 +51,7 @@ func TestCLITestSuite(t *testing.T) { func (s *CLITestSuite) SetupSuite() { s.encCfg = testutilmod.MakeTestEncodingConfig(gov.AppModuleBasic{}) + s.ac = s.encCfg.InterfaceRegistry.SigningContext().AddressCodec() s.kr = keyring.NewInMemory(s.encCfg.Codec) s.baseCtx = client.Context{}. WithKeyring(s.kr). @@ -119,15 +122,6 @@ func (s *CLITestSuite) TestNewTxCmdSendNFT() { }, false, }, - "amount out of range": { - []string{ - s.contractID, - s.stranger.Address.String(), - s.customer.Address.String(), - fmt.Sprintf("%s:1%0127d", s.classID, 0), - }, - false, - }, "invalid contract id": { []string{ "", @@ -143,7 +137,7 @@ func (s *CLITestSuite) TestNewTxCmdSendNFT() { tc := tc s.Run(name, func() { - cmd := cli.NewTxCmdSendNFT() + cmd := cli.NewTxCmdSendNFT(s.ac) out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, append(tc.args, s.commonFlags...)) if !tc.valid { s.Require().Error(err) @@ -193,16 +187,6 @@ func (s *CLITestSuite) TestNewTxCmdOperatorSendNFT() { }, false, }, - "amount out of range": { - []string{ - s.contractID, - s.operator.Address.String(), - s.vendor.Address.String(), - s.customer.Address.String(), - fmt.Sprintf("%s:1%0127d", s.classID, 0), - }, - false, - }, "invalid contract id": { []string{ "", @@ -219,7 +203,7 @@ func (s *CLITestSuite) TestNewTxCmdOperatorSendNFT() { tc := tc s.Run(name, func() { - cmd := cli.NewTxCmdOperatorSendNFT() + cmd := cli.NewTxCmdOperatorSendNFT(s.ac) out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, append(tc.args, s.commonFlags...)) if !tc.valid { s.Require().Error(err) @@ -386,7 +370,7 @@ func (s *CLITestSuite) TestNewTxCmdMintNFT() { tc := tc s.Run(name, func() { - cmd := cli.NewTxCmdMintNFT() + cmd := cli.NewTxCmdMintNFT(s.ac) out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, append(tc.args, s.commonFlags...)) if !tc.valid { s.Require().Error(err) @@ -506,7 +490,7 @@ func (s *CLITestSuite) TestNewTxCmdOperatorOperatorBurnNFT() { tc := tc s.Run(name, func() { - cmd := cli.NewTxCmdOperatorBurnNFT() + cmd := cli.NewTxCmdOperatorBurnNFT(s.ac) out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, append(tc.args, s.commonFlags...)) if !tc.valid { s.Require().Error(err) @@ -627,7 +611,7 @@ func (s *CLITestSuite) TestNewTxCmdGrantPermission() { tc := tc s.Run(name, func() { - cmd := cli.NewTxCmdGrantPermission() + cmd := cli.NewTxCmdGrantPermission(s.ac) out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, append(tc.args, s.commonFlags...)) if !tc.valid { s.Require().Error(err) @@ -725,7 +709,7 @@ func (s *CLITestSuite) TestNewTxCmdAuthorizeOperator() { tc := tc s.Run(name, func() { - cmd := cli.NewTxCmdAuthorizeOperator() + cmd := cli.NewTxCmdAuthorizeOperator(s.ac) out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, append(tc.args, s.commonFlags...)) if !tc.valid { s.Require().Error(err) @@ -774,7 +758,7 @@ func (s *CLITestSuite) TestNewTxCmdRevokeOperator() { tc := tc s.Run(name, func() { - cmd := cli.NewTxCmdRevokeOperator() + cmd := cli.NewTxCmdRevokeOperator(s.ac) out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, append(tc.args, s.commonFlags...)) if !tc.valid { s.Require().Error(err) diff --git a/x/collection/collection.go b/x/collection/collection.go index 2f1323feba..b5ea12e291 100644 --- a/x/collection/collection.go +++ b/x/collection/collection.go @@ -107,10 +107,10 @@ func (c NFTClass) ValidateBasic() error { return err } - if err := validateName(c.Name); err != nil { + if err := ValidateName(c.Name); err != nil { return err } - if err := validateMeta(c.Meta); err != nil { + if err := ValidateMeta(c.Meta); err != nil { return err } diff --git a/x/collection/genesis.go b/x/collection/genesis.go index ca12808e9d..25308ab1b6 100644 --- a/x/collection/genesis.go +++ b/x/collection/genesis.go @@ -3,6 +3,7 @@ package collection import ( "math" + "cosmossdk.io/core/address" cmath "cosmossdk.io/math" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -11,7 +12,7 @@ import ( ) // ValidateGenesis check the given genesis state has no integrity issues -func ValidateGenesis(data GenesisState) error { +func ValidateGenesis(data GenesisState, addressCodec address.Codec) error { if err := validateParams(data.Params); err != nil { return err } @@ -21,13 +22,13 @@ func ValidateGenesis(data GenesisState) error { return err } - if err := validateName(contract.Name); err != nil { + if err := ValidateName(contract.Name); err != nil { return err } - if err := validateURI(contract.Uri); err != nil { + if err := ValidateURI(contract.Uri); err != nil { return err } - if err := validateMeta(contract.Meta); err != nil { + if err := ValidateMeta(contract.Meta); err != nil { return err } } @@ -100,10 +101,10 @@ func ValidateGenesis(data GenesisState) error { if err := ValidateTokenID(token.TokenId); err != nil { return err } - if err := validateName(token.Name); err != nil { + if err := ValidateName(token.Name); err != nil { return err } - if err := validateMeta(token.Meta); err != nil { + if err := ValidateMeta(token.Meta); err != nil { return err } } @@ -118,10 +119,10 @@ func ValidateGenesis(data GenesisState) error { return sdkerrors.ErrInvalidRequest.Wrap("authorizations cannot be empty") } for _, authorization := range contractAuthorizations.Authorizations { - if _, err := sdk.AccAddressFromBech32(authorization.Holder); err != nil { + if _, err := addressCodec.StringToBytes(authorization.Holder); err != nil { return err } - if _, err := sdk.AccAddressFromBech32(authorization.Operator); err != nil { + if _, err := addressCodec.StringToBytes(authorization.Operator); err != nil { return err } } @@ -136,7 +137,7 @@ func ValidateGenesis(data GenesisState) error { return sdkerrors.ErrInvalidRequest.Wrap("grants cannot be empty") } for _, grant := range contractGrants.Grants { - if _, err := sdk.AccAddressFromBech32(grant.Grantee); err != nil { + if _, err := addressCodec.StringToBytes(grant.Grantee); err != nil { return err } if err := ValidatePermission(grant.Permission); err != nil { diff --git a/x/collection/genesis_test.go b/x/collection/genesis_test.go index cf35f7fb9a..c65d92b308 100644 --- a/x/collection/genesis_test.go +++ b/x/collection/genesis_test.go @@ -10,12 +10,14 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" sdk "github.com/cosmos/cosmos-sdk/types" + authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" "github.com/Finschia/finschia-sdk/x/collection" ) func TestValidateGenesis(t *testing.T) { addr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) + ac := authcodec.NewBech32Codec("cosmos") testCases := map[string]struct { gs *collection.GenesisState valid bool @@ -410,7 +412,7 @@ func TestValidateGenesis(t *testing.T) { for name, tc := range testCases { t.Run(name, func(t *testing.T) { - err := collection.ValidateGenesis(*tc.gs) + err := collection.ValidateGenesis(*tc.gs, ac) if !tc.valid { require.Error(t, err) return diff --git a/x/collection/keeper/genesis.go b/x/collection/keeper/genesis.go index b5b98975c7..972e841627 100644 --- a/x/collection/keeper/genesis.go +++ b/x/collection/keeper/genesis.go @@ -3,6 +3,7 @@ package keeper import ( "fmt" + "cosmossdk.io/core/address" "cosmossdk.io/log" "cosmossdk.io/math" @@ -61,7 +62,7 @@ func (p *ProgressReporter) Tick() { } // InitGenesis new collection genesis -func (k Keeper) InitGenesis(ctx sdk.Context, data *collection.GenesisState) { +func (k Keeper) InitGenesis(ctx sdk.Context, data *collection.GenesisState, addressCodec address.Codec) { k.SetParams(ctx, data.Params) reporter := newProgressReporter(k.Logger(ctx), "import contract", len(data.Contracts)) @@ -111,7 +112,7 @@ func (k Keeper) InitGenesis(ctx sdk.Context, data *collection.GenesisState) { for _, balance := range contractBalances.Balances { for _, coin := range balance.Amount { - addr, err := sdk.AccAddressFromBech32(balance.Address) + addr, err := addressCodec.StringToBytes(balance.Address) if err != nil { panic(err) } @@ -141,11 +142,11 @@ func (k Keeper) InitGenesis(ctx sdk.Context, data *collection.GenesisState) { reporter = newProgressReporter(k.Logger(ctx), "import authorizations", len(data.Authorizations)) for _, contractAuthorizations := range data.Authorizations { for _, authorization := range contractAuthorizations.Authorizations { - holderAddr, err := sdk.AccAddressFromBech32(authorization.Holder) + holderAddr, err := addressCodec.StringToBytes(authorization.Holder) if err != nil { panic(err) } - operatorAddr, err := sdk.AccAddressFromBech32(authorization.Operator) + operatorAddr, err := addressCodec.StringToBytes(authorization.Operator) if err != nil { panic(err) } @@ -158,7 +159,7 @@ func (k Keeper) InitGenesis(ctx sdk.Context, data *collection.GenesisState) { reporter = newProgressReporter(k.Logger(ctx), "import grants", len(data.Grants)) for _, contractGrants := range data.Grants { for _, grant := range contractGrants.Grants { - granteeAddr, err := sdk.AccAddressFromBech32(grant.Grantee) + granteeAddr, err := addressCodec.StringToBytes(grant.Grantee) if err != nil { panic(err) } diff --git a/x/collection/keeper/genesis_test.go b/x/collection/keeper/genesis_test.go index 048c5ef9e1..8311ec90dd 100644 --- a/x/collection/keeper/genesis_test.go +++ b/x/collection/keeper/genesis_test.go @@ -16,7 +16,7 @@ func (s *KeeperTestSuite) TestImportExportGenesis() { s.keeper.Abandon(ctx, s.contractID, s.vendor, collection.PermissionMint) - s.keeper.InitGenesis(ctx, genesis) + s.keeper.InitGenesis(ctx, genesis, s.addressCodec) // export again and compare newGenesis := s.keeper.ExportGenesis(s.ctx) diff --git a/x/collection/keeper/grpc_query.go b/x/collection/keeper/grpc_query.go index 48903c083b..042e24f32e 100644 --- a/x/collection/keeper/grpc_query.go +++ b/x/collection/keeper/grpc_query.go @@ -33,7 +33,7 @@ func NewQueryServer(keeper Keeper) collection.QueryServer { } func (s queryServer) addressFromBech32GRPC(bech32, context string) (sdk.AccAddress, error) { - addr, err := sdk.AccAddressFromBech32(bech32) + addr, err := s.keeper.addressCodec.StringToBytes(bech32) if err != nil { return nil, status.Error(codes.InvalidArgument, errorsmod.Wrap(sdkerrors.ErrInvalidAddress.Wrap(bech32), context).Error()) } diff --git a/x/collection/keeper/keeper_test.go b/x/collection/keeper/keeper_test.go index 29cc58b76a..a60d5ba89d 100644 --- a/x/collection/keeper/keeper_test.go +++ b/x/collection/keeper/keeper_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/suite" + "cosmossdk.io/core/address" "cosmossdk.io/log" "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" @@ -27,10 +28,11 @@ import ( type KeeperTestSuite struct { suite.Suite - ctx sdk.Context - keeper keeper.Keeper - queryServer collection.QueryServer - msgServer collection.MsgServer + ctx sdk.Context + keeper keeper.Keeper + queryServer collection.QueryServer + msgServer collection.MsgServer + addressCodec address.Codec vendor sdk.AccAddress operator sdk.AccAddress @@ -50,16 +52,16 @@ func (s *KeeperTestSuite) createRandomAccounts(accNum int) []sdk.AccAddress { seenAddresses := make(map[string]bool, accNum) addresses := make([]sdk.AccAddress, accNum) for i := range addresses { - var address sdk.AccAddress + var addr sdk.AccAddress for { pk := secp256k1.GenPrivKey().PubKey() - address = sdk.AccAddress(pk.Address()) - if !seenAddresses[address.String()] { - seenAddresses[address.String()] = true + addr = sdk.AccAddress(pk.Address()) + if !seenAddresses[addr.String()] { + seenAddresses[addr.String()] = true break } } - addresses[i] = address + addresses[i] = addr } return addresses } @@ -131,6 +133,9 @@ func (s *KeeperTestSuite) prepareInitialSetup() { ValAddressPrefix: "linkvaloper", }.NewInterfaceRegistry() encCfg.Codec = codec.NewProtoCodec(encCfg.InterfaceRegistry) + sdk.GetConfig().SetBech32PrefixForAccount("link", "linkpub") + sdk.GetConfig().SetBech32PrefixForValidator("linkvaloper", "linkvaloperpub") + sdk.GetConfig().SetBech32PrefixForConsensusNode("linkvalcons", "linkvalconspub") collection.RegisterInterfaces(encCfg.InterfaceRegistry) testdata.RegisterInterfaces(encCfg.InterfaceRegistry) @@ -145,7 +150,8 @@ func (s *KeeperTestSuite) prepareInitialSetup() { bapp.SetInterfaceRegistry(encCfg.InterfaceRegistry) s.keeper = keeper.NewKeeper(encCfg.Codec, kvStoreService) - s.keeper.InitGenesis(s.ctx, collection.DefaultGenesisState()) + s.addressCodec = encCfg.Codec.InterfaceRegistry().SigningContext().AddressCodec() + s.keeper.InitGenesis(s.ctx, collection.DefaultGenesisState(), s.addressCodec) s.keeper.SetParams(s.ctx, collection.Params{}) s.queryServer = keeper.NewQueryServer(s.keeper) @@ -157,8 +163,8 @@ func (s *KeeperTestSuite) prepareInitialSetup() { &s.customer, &s.stranger, } - for i, address := range s.createRandomAccounts(len(addresses)) { - *addresses[i] = address + for i, addr := range s.createRandomAccounts(len(addresses)) { + *addresses[i] = addr } } diff --git a/x/collection/keeper/msg_server.go b/x/collection/keeper/msg_server.go index a995ed605a..86e043c1c6 100644 --- a/x/collection/keeper/msg_server.go +++ b/x/collection/keeper/msg_server.go @@ -28,13 +28,36 @@ func NewMsgServer(keeper Keeper) collection.MsgServer { var _ collection.MsgServer = (*msgServer)(nil) func (s msgServer) SendNFT(c context.Context, req *collection.MsgSendNFT) (*collection.MsgSendNFTResponse, error) { + if err := collection.ValidateContractID(req.ContractId); err != nil { + return nil, err + } + + if _, err := s.keeper.addressCodec.StringToBytes(req.From); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", req.From) + } + if _, err := s.keeper.addressCodec.StringToBytes(req.To); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid to address: %s", req.To) + } + + if len(req.TokenIds) == 0 { + return nil, collection.ErrEmptyField.Wrap("token ids cannot be empty") + } + for _, id := range req.TokenIds { + if err := collection.ValidateTokenID(id); err != nil { + return nil, err + } + } + ctx := sdk.UnwrapSDKContext(c) if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil { return nil, err } - fromAddr := sdk.MustAccAddressFromBech32(req.From) + fromAddr, err := s.keeper.addressCodec.StringToBytes(req.From) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrap(err.Error()) + } amount := make([]collection.Coin, len(req.TokenIds)) for i, id := range req.TokenIds { @@ -44,12 +67,15 @@ func (s msgServer) SendNFT(c context.Context, req *collection.MsgSendNFT) (*coll if err := s.keeper.hasNFT(ctx, req.ContractId, id); err != nil { return nil, err } - if !s.keeper.getOwner(ctx, req.ContractId, id).Equals(fromAddr) { + if !s.keeper.getOwner(ctx, req.ContractId, id).Equals(sdk.AccAddress(fromAddr)) { return nil, collection.ErrTokenNotOwnedBy.Wrapf("%s does not have %s", fromAddr, id) } } - toAddr := sdk.MustAccAddressFromBech32(req.To) + toAddr, err := s.keeper.addressCodec.StringToBytes(req.To) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid to address: %s", err) + } if err := s.keeper.SendCoins(ctx, req.ContractId, fromAddr, toAddr, amount); err != nil { panic(err) @@ -70,14 +96,43 @@ func (s msgServer) SendNFT(c context.Context, req *collection.MsgSendNFT) (*coll } func (s msgServer) OperatorSendNFT(c context.Context, req *collection.MsgOperatorSendNFT) (*collection.MsgOperatorSendNFTResponse, error) { + if err := collection.ValidateContractID(req.ContractId); err != nil { + return nil, err + } + + if _, err := s.keeper.addressCodec.StringToBytes(req.Operator); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid operator address: %s", req.Operator) + } + if _, err := s.keeper.addressCodec.StringToBytes(req.From); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", req.From) + } + if _, err := s.keeper.addressCodec.StringToBytes(req.To); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid to address: %s", req.To) + } + + if len(req.TokenIds) == 0 { + return nil, collection.ErrEmptyField.Wrap("token ids cannot be empty") + } + for _, id := range req.TokenIds { + if err := collection.ValidateTokenID(id); err != nil { + return nil, err + } + } + ctx := sdk.UnwrapSDKContext(c) if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil { return nil, err } - operatorAddr := sdk.MustAccAddressFromBech32(req.Operator) - fromAddr := sdk.MustAccAddressFromBech32(req.From) + operatorAddr, err := s.keeper.addressCodec.StringToBytes(req.Operator) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid operator address: %s", err) + } + fromAddr, err := s.keeper.addressCodec.StringToBytes(req.From) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", err) + } if _, err := s.keeper.GetAuthorization(ctx, req.ContractId, fromAddr, operatorAddr); err != nil { return nil, collection.ErrCollectionNotApproved.Wrap(err.Error()) @@ -91,12 +146,15 @@ func (s msgServer) OperatorSendNFT(c context.Context, req *collection.MsgOperato if err := s.keeper.hasNFT(ctx, req.ContractId, id); err != nil { return nil, err } - if !s.keeper.getOwner(ctx, req.ContractId, id).Equals(fromAddr) { + if !s.keeper.getOwner(ctx, req.ContractId, id).Equals(sdk.AccAddress(fromAddr)) { return nil, collection.ErrTokenNotOwnedBy.Wrapf("%s does not have %s", fromAddr, id) } } - toAddr := sdk.MustAccAddressFromBech32(req.To) + toAddr, err := s.keeper.addressCodec.StringToBytes(req.To) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", err) + } if err := s.keeper.SendCoins(ctx, req.ContractId, fromAddr, toAddr, amount); err != nil { panic(err) @@ -117,14 +175,35 @@ func (s msgServer) OperatorSendNFT(c context.Context, req *collection.MsgOperato } func (s msgServer) AuthorizeOperator(c context.Context, req *collection.MsgAuthorizeOperator) (*collection.MsgAuthorizeOperatorResponse, error) { + if err := collection.ValidateContractID(req.ContractId); err != nil { + return nil, err + } + + if _, err := s.keeper.addressCodec.StringToBytes(req.Holder); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid holder address: %s", req.Holder) + } + if _, err := s.keeper.addressCodec.StringToBytes(req.Operator); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid operator address: %s", req.Operator) + } + + if req.Operator == req.Holder { + return nil, collection.ErrApproverProxySame + } + ctx := sdk.UnwrapSDKContext(c) if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil { return nil, err } - holderAddr := sdk.MustAccAddressFromBech32(req.Holder) - operatorAddr := sdk.MustAccAddressFromBech32(req.Operator) + holderAddr, err := s.keeper.addressCodec.StringToBytes(req.Holder) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid holder address: %s", err) + } + operatorAddr, err := s.keeper.addressCodec.StringToBytes(req.Operator) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid operator address: %s", err) + } if err := s.keeper.AuthorizeOperator(ctx, req.ContractId, holderAddr, operatorAddr); err != nil { return nil, err @@ -143,14 +222,35 @@ func (s msgServer) AuthorizeOperator(c context.Context, req *collection.MsgAutho } func (s msgServer) RevokeOperator(c context.Context, req *collection.MsgRevokeOperator) (*collection.MsgRevokeOperatorResponse, error) { + if err := collection.ValidateContractID(req.ContractId); err != nil { + return nil, err + } + + if _, err := s.keeper.addressCodec.StringToBytes(req.Holder); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid holder address: %s", req.Holder) + } + if _, err := s.keeper.addressCodec.StringToBytes(req.Operator); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid operator address: %s", req.Operator) + } + + if req.Operator == req.Holder { + return nil, collection.ErrApproverProxySame + } + ctx := sdk.UnwrapSDKContext(c) if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil { return nil, err } - holderAddr := sdk.MustAccAddressFromBech32(req.Holder) - operatorAddr := sdk.MustAccAddressFromBech32(req.Operator) + holderAddr, err := s.keeper.addressCodec.StringToBytes(req.Holder) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid holder address: %s", err) + } + operatorAddr, err := s.keeper.addressCodec.StringToBytes(req.Operator) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid operator address: %s", err) + } if err := s.keeper.RevokeOperator(ctx, req.ContractId, holderAddr, operatorAddr); err != nil { return nil, err @@ -169,6 +269,22 @@ func (s msgServer) RevokeOperator(c context.Context, req *collection.MsgRevokeOp } func (s msgServer) CreateContract(c context.Context, req *collection.MsgCreateContract) (*collection.MsgCreateContractResponse, error) { + if _, err := s.keeper.addressCodec.StringToBytes(req.Owner); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid owner address: %s", req.Owner) + } + + if err := collection.ValidateName(req.Name); err != nil { + return nil, err + } + + if err := collection.ValidateURI(req.Uri); err != nil { + return nil, err + } + + if err := collection.ValidateMeta(req.Meta); err != nil { + return nil, err + } + ctx := sdk.UnwrapSDKContext(c) contract := collection.Contract{ @@ -176,7 +292,10 @@ func (s msgServer) CreateContract(c context.Context, req *collection.MsgCreateCo Uri: req.Uri, Meta: req.Meta, } - ownerAddr := sdk.MustAccAddressFromBech32(req.Owner) + ownerAddr, err := s.keeper.addressCodec.StringToBytes(req.Owner) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid owner address: %s", err) + } id := s.keeper.CreateContract(ctx, ownerAddr, contract) @@ -184,13 +303,32 @@ func (s msgServer) CreateContract(c context.Context, req *collection.MsgCreateCo } func (s msgServer) IssueNFT(c context.Context, req *collection.MsgIssueNFT) (*collection.MsgIssueNFTResponse, error) { + if err := collection.ValidateContractID(req.ContractId); err != nil { + return nil, err + } + + if err := collection.ValidateName(req.Name); err != nil { + return nil, err + } + + if err := collection.ValidateMeta(req.Meta); err != nil { + return nil, err + } + + if _, err := s.keeper.addressCodec.StringToBytes(req.Owner); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid owner address: %s", req.Owner) + } + ctx := sdk.UnwrapSDKContext(c) if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil { return nil, err } - ownerAddr := sdk.MustAccAddressFromBech32(req.Owner) + ownerAddr, err := s.keeper.addressCodec.StringToBytes(req.Owner) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid owner address: %s", err) + } if _, err := s.keeper.GetGrant(ctx, req.ContractId, ownerAddr, collection.PermissionIssue); err != nil { return nil, collection.ErrTokenNoPermission.Wrap(err.Error()) @@ -227,19 +365,57 @@ func (s msgServer) IssueNFT(c context.Context, req *collection.MsgIssueNFT) (*co } func (s msgServer) MintNFT(c context.Context, req *collection.MsgMintNFT) (*collection.MsgMintNFTResponse, error) { + if err := collection.ValidateContractID(req.ContractId); err != nil { + return nil, err + } + + if _, err := s.keeper.addressCodec.StringToBytes(req.From); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", req.From) + } + if _, err := s.keeper.addressCodec.StringToBytes(req.To); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid to address: %s", req.To) + } + + if len(req.Params) == 0 { + return nil, collection.ErrEmptyField.Wrap("mint params cannot be empty") + } + for _, param := range req.Params { + classID := param.TokenType + if err := collection.ValidateLegacyNFTClassID(classID); err != nil { + return nil, err + } + + if len(param.Name) == 0 { + return nil, collection.ErrInvalidTokenName + } + if err := collection.ValidateName(param.Name); err != nil { + return nil, err + } + + if err := collection.ValidateMeta(param.Meta); err != nil { + return nil, err + } + } + ctx := sdk.UnwrapSDKContext(c) if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil { return nil, err } - fromAddr := sdk.MustAccAddressFromBech32(req.From) + fromAddr, err := s.keeper.addressCodec.StringToBytes(req.From) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", err) + } if _, err := s.keeper.GetGrant(ctx, req.ContractId, fromAddr, collection.PermissionMint); err != nil { return nil, collection.ErrTokenNoPermission.Wrap(err.Error()) } - toAddr := sdk.MustAccAddressFromBech32(req.To) + toAddr, err := s.keeper.addressCodec.StringToBytes(req.To) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid to address: %s", err) + } tokens, err := s.keeper.MintNFT(ctx, req.ContractId, toAddr, req.Params) if err != nil { @@ -264,13 +440,33 @@ func (s msgServer) MintNFT(c context.Context, req *collection.MsgMintNFT) (*coll } func (s msgServer) BurnNFT(c context.Context, req *collection.MsgBurnNFT) (*collection.MsgBurnNFTResponse, error) { + if err := collection.ValidateContractID(req.ContractId); err != nil { + return nil, err + } + + if _, err := s.keeper.addressCodec.StringToBytes(req.From); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", req.From) + } + + if len(req.TokenIds) == 0 { + return nil, collection.ErrEmptyField.Wrap("token ids cannot be empty") + } + for _, id := range req.TokenIds { + if err := collection.ValidateLegacyNFTID(id); err != nil { + return nil, err + } + } + ctx := sdk.UnwrapSDKContext(c) if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil { return nil, err } - fromAddr := sdk.MustAccAddressFromBech32(req.From) + fromAddr, err := s.keeper.addressCodec.StringToBytes(req.From) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", err) + } if _, err := s.keeper.GetGrant(ctx, req.ContractId, fromAddr, collection.PermissionBurn); err != nil { return nil, collection.ErrTokenNoPermission.Wrap(err.Error()) @@ -284,7 +480,7 @@ func (s msgServer) BurnNFT(c context.Context, req *collection.MsgBurnNFT) (*coll if err := s.keeper.hasNFT(ctx, req.ContractId, id); err != nil { return nil, err } - if !s.keeper.getOwner(ctx, req.ContractId, id).Equals(fromAddr) { + if !s.keeper.getOwner(ctx, req.ContractId, id).Equals(sdk.AccAddress(fromAddr)) { return nil, collection.ErrTokenNotOwnedBy.Wrapf("%s does not have %s", fromAddr, id) } } @@ -309,14 +505,40 @@ func (s msgServer) BurnNFT(c context.Context, req *collection.MsgBurnNFT) (*coll } func (s msgServer) OperatorBurnNFT(c context.Context, req *collection.MsgOperatorBurnNFT) (*collection.MsgOperatorBurnNFTResponse, error) { + if err := collection.ValidateContractID(req.ContractId); err != nil { + return nil, err + } + + if _, err := s.keeper.addressCodec.StringToBytes(req.Operator); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid operator address: %s", req.Operator) + } + if _, err := s.keeper.addressCodec.StringToBytes(req.From); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", req.From) + } + + if len(req.TokenIds) == 0 { + return nil, collection.ErrEmptyField.Wrap("token ids cannot be empty") + } + for _, id := range req.TokenIds { + if err := collection.ValidateLegacyNFTID(id); err != nil { + return nil, err + } + } + ctx := sdk.UnwrapSDKContext(c) if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil { return nil, err } - fromAddr := sdk.MustAccAddressFromBech32(req.From) - operatorAddr := sdk.MustAccAddressFromBech32(req.Operator) + fromAddr, err := s.keeper.addressCodec.StringToBytes(req.From) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", err) + } + operatorAddr, err := s.keeper.addressCodec.StringToBytes(req.Operator) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid operator address: %s", err) + } if _, err := s.keeper.GetAuthorization(ctx, req.ContractId, fromAddr, operatorAddr); err != nil { return nil, collection.ErrCollectionNotApproved.Wrap(err.Error()) @@ -334,7 +556,7 @@ func (s msgServer) OperatorBurnNFT(c context.Context, req *collection.MsgOperato if err := s.keeper.hasNFT(ctx, req.ContractId, id); err != nil { return nil, err } - if !s.keeper.getOwner(ctx, req.ContractId, id).Equals(fromAddr) { + if !s.keeper.getOwner(ctx, req.ContractId, id).Equals(sdk.AccAddress(fromAddr)) { return nil, collection.ErrTokenNotOwnedBy.Wrapf("%s does not have %s", fromAddr, id) } } @@ -359,14 +581,80 @@ func (s msgServer) OperatorBurnNFT(c context.Context, req *collection.MsgOperato } func (s msgServer) Modify(c context.Context, req *collection.MsgModify) (*collection.MsgModifyResponse, error) { + for i, change := range req.Changes { + key := change.Key + converted := collection.AttrCanonicalKey(key) + if converted != key { + req.Changes[i].Key = converted + } + } + if err := collection.ValidateContractID(req.ContractId); err != nil { + return nil, err + } + + if _, err := s.keeper.addressCodec.StringToBytes(req.Owner); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid owner address: %s", req.Owner) + } + + if len(req.TokenType) != 0 { + classID := req.TokenType + if err := collection.ValidateClassID(classID); err != nil { + return nil, collection.ErrInvalidTokenType.Wrap(err.Error()) + } + } + + if len(req.TokenIndex) != 0 { + tokenID := req.TokenType + req.TokenIndex + if err := collection.ValidateTokenID(tokenID); err != nil { + return nil, collection.ErrInvalidTokenIndex.Wrap(err.Error()) + } + // reject modifying nft class with token index filled (daphne compat.) + if collection.ValidateLegacyIdxNFT(tokenID) == nil { + return nil, collection.ErrInvalidTokenIndex.Wrap("cannot modify nft class with index filled") + } + } + + validator := collection.ValidateTokenClassChange + if len(req.TokenType) == 0 { + if len(req.TokenIndex) == 0 { + validator = collection.ValidateContractChange + } else { + return nil, collection.ErrTokenIndexWithoutType.Wrap("token index without type") + } + } + if len(req.Changes) == 0 { + return nil, collection.ErrEmptyChanges.Wrap("empty changes") + } + if len(req.Changes) > collection.ChangesLimit { + return nil, collection.ErrInvalidChangesFieldCount.Wrapf("the number of changes exceeds the limit: %d > %d", len(req.Changes), collection.ChangesLimit) + } + seenKeys := map[string]bool{} + for _, change := range req.Changes { + key := change.Key + if seenKeys[key] { + return nil, collection.ErrDuplicateChangesField.Wrapf("duplicate keys: %s", change.Key) + } + seenKeys[key] = true + + attribute := collection.Attribute{ + Key: change.Key, + Value: change.Value, + } + if err := validator(attribute); err != nil { + return nil, err + } + } + ctx := sdk.UnwrapSDKContext(c) - collection.UpdateMsgModify(req) if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil { return nil, err } - operator := sdk.MustAccAddressFromBech32(req.Owner) + operator, err := s.keeper.addressCodec.StringToBytes(req.Owner) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid operator address: %s", err) + } if _, err := s.keeper.GetGrant(ctx, req.ContractId, operator, collection.PermissionModify); err != nil { return nil, collection.ErrTokenNoPermission.Wrap(err.Error()) @@ -388,7 +676,7 @@ func (s msgServer) Modify(c context.Context, req *collection.MsgModify) (*collec if tokenIndex != "" && collection.ValidateNFTID(tokenID) == nil { event := collection.EventModifiedNFT{ ContractId: req.ContractId, - Operator: operator.String(), + Operator: req.Owner, TokenId: tokenID, Changes: changes, } @@ -401,7 +689,7 @@ func (s msgServer) Modify(c context.Context, req *collection.MsgModify) (*collec event := collection.EventModifiedTokenClass{ ContractId: req.ContractId, - Operator: operator.String(), + Operator: req.Owner, TokenType: classID, Changes: changes, TypeName: proto.MessageName(&collection.NFTClass{}), @@ -415,7 +703,7 @@ func (s msgServer) Modify(c context.Context, req *collection.MsgModify) (*collec if req.TokenIndex == "" { event := collection.EventModifiedContract{ ContractId: req.ContractId, - Operator: operator.String(), + Operator: req.Owner, Changes: changes, } if err := ctx.EventManager().EmitTypedEvent(&event); err != nil { @@ -437,14 +725,35 @@ func (s msgServer) Modify(c context.Context, req *collection.MsgModify) (*collec } func (s msgServer) GrantPermission(c context.Context, req *collection.MsgGrantPermission) (*collection.MsgGrantPermissionResponse, error) { + if err := collection.ValidateContractID(req.ContractId); err != nil { + return nil, err + } + + if _, err := s.keeper.addressCodec.StringToBytes(req.From); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", req.From) + } + if _, err := s.keeper.addressCodec.StringToBytes(req.To); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid to address: %s", req.To) + } + + if err := collection.ValidateLegacyPermission(req.Permission); err != nil { + return nil, err + } + ctx := sdk.UnwrapSDKContext(c) if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil { return nil, err } - granter := sdk.MustAccAddressFromBech32(req.From) - grantee := sdk.MustAccAddressFromBech32(req.To) + granter, err := s.keeper.addressCodec.StringToBytes(req.From) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", err) + } + grantee, err := s.keeper.addressCodec.StringToBytes(req.To) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid to address: %s", err) + } permission := collection.Permission(collection.LegacyPermissionFromString(req.Permission)) if _, err := s.keeper.GetGrant(ctx, req.ContractId, granter, permission); err != nil { @@ -458,13 +767,28 @@ func (s msgServer) GrantPermission(c context.Context, req *collection.MsgGrantPe } func (s msgServer) RevokePermission(c context.Context, req *collection.MsgRevokePermission) (*collection.MsgRevokePermissionResponse, error) { + if err := collection.ValidateContractID(req.ContractId); err != nil { + return nil, err + } + + if _, err := s.keeper.addressCodec.StringToBytes(req.From); err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", req.From) + } + + if err := collection.ValidateLegacyPermission(req.Permission); err != nil { + return nil, err + } + ctx := sdk.UnwrapSDKContext(c) if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil { return nil, err } - grantee := sdk.MustAccAddressFromBech32(req.From) + grantee, err := s.keeper.addressCodec.StringToBytes(req.From) + if err != nil { + return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", err) + } permission := collection.Permission(collection.LegacyPermissionFromString(req.Permission)) if _, err := s.keeper.GetGrant(ctx, req.ContractId, grantee, permission); err != nil { diff --git a/x/collection/keeper/msg_server_test.go b/x/collection/keeper/msg_server_test.go index 9d838f93c4..b7b9802f4a 100644 --- a/x/collection/keeper/msg_server_test.go +++ b/x/collection/keeper/msg_server_test.go @@ -3,12 +3,15 @@ package keeper_test import ( "encoding/json" "fmt" + "strings" abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/gogoproto/proto" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/Finschia/finschia-sdk/x/collection" ) @@ -38,12 +41,16 @@ func (s *KeeperTestSuite) TestMsgSendNFT() { testCases := map[string]struct { contractID string tokenID string + from string + to string err error events sdk.Events }{ "valid request": { contractID: s.contractID, tokenID: rootNFTID, + from: s.customer.String(), + to: s.vendor.String(), events: sdk.Events{ sdk.Event{ Type: "lbm.collection.v1.EventSent", @@ -60,29 +67,75 @@ func (s *KeeperTestSuite) TestMsgSendNFT() { "contract not found": { contractID: "deadbeef", tokenID: collection.NewNFTID(s.nftClassID, 1), + from: s.customer.String(), + to: s.vendor.String(), err: collection.ErrContractNotExist, }, - "not found": { + "NFT not found": { contractID: s.contractID, tokenID: collection.NewNFTID("deadbeef", 1), + from: s.customer.String(), + to: s.vendor.String(), err: collection.ErrTokenNotExist, }, "not owned by": { contractID: s.contractID, tokenID: collection.NewNFTID(s.nftClassID, s.numNFTs+1), + from: s.customer.String(), + to: s.vendor.String(), err: collection.ErrTokenNotOwnedBy, }, + "invalid from": { + contractID: s.contractID, + tokenID: rootNFTID, + to: s.vendor.String(), + err: sdkerrors.ErrInvalidAddress, + }, + "invalid contract id": { + tokenID: rootNFTID, + from: s.customer.String(), + to: s.vendor.String(), + err: collection.ErrInvalidContractID, + }, + "invalid to": { + contractID: s.contractID, + tokenID: rootNFTID, + from: s.customer.String(), + err: sdkerrors.ErrInvalidAddress, + }, + "empty token ids": { + contractID: s.contractID, + from: s.customer.String(), + to: s.vendor.String(), + err: collection.ErrEmptyField, + }, + "invalid token ids": { + contractID: s.contractID, + tokenID: "null", + from: s.customer.String(), + to: s.vendor.String(), + err: collection.ErrInvalidTokenID, + }, } for name, tc := range testCases { s.Run(name, func() { ctx, _ := s.ctx.CacheContext() - req := &collection.MsgSendNFT{ - ContractId: tc.contractID, - From: s.customer.String(), - To: s.vendor.String(), - TokenIds: []string{tc.tokenID}, + var req *collection.MsgSendNFT + if tc.tokenID == "" { + req = &collection.MsgSendNFT{ + ContractId: tc.contractID, + From: tc.from, + To: tc.to, + } + } else { + req = &collection.MsgSendNFT{ + ContractId: tc.contractID, + From: tc.from, + To: tc.to, + TokenIds: []string{tc.tokenID}, + } } res, err := s.msgServer.SendNFT(ctx, req) s.Require().ErrorIs(err, tc.err) @@ -101,16 +154,18 @@ func (s *KeeperTestSuite) TestMsgOperatorSendNFT() { testCases := map[string]struct { contractID string - operator sdk.AccAddress - from sdk.AccAddress + operator string + from string + to string tokenID string err error events sdk.Events }{ "valid request": { contractID: s.contractID, - operator: s.operator, - from: s.customer, + operator: s.operator.String(), + from: s.customer.String(), + to: s.vendor.String(), tokenID: rootNFTID, events: sdk.Events{ sdk.Event{ @@ -127,44 +182,101 @@ func (s *KeeperTestSuite) TestMsgOperatorSendNFT() { }, "contract not found": { contractID: "deadbeef", - operator: s.operator, - from: s.customer, + operator: s.operator.String(), + from: s.customer.String(), + to: s.vendor.String(), tokenID: rootNFTID, err: collection.ErrContractNotExist, }, "not approved": { contractID: s.contractID, - operator: s.vendor, - from: s.customer, + operator: s.vendor.String(), + from: s.customer.String(), + to: s.vendor.String(), tokenID: rootNFTID, err: collection.ErrCollectionNotApproved, }, - "not found": { + "NFT not found": { contractID: s.contractID, - operator: s.operator, - from: s.customer, + operator: s.operator.String(), + from: s.customer.String(), + to: s.vendor.String(), tokenID: collection.NewNFTID("deadbeef", 1), err: collection.ErrTokenNotExist, }, "not owned by": { contractID: s.contractID, - operator: s.operator, - from: s.customer, + operator: s.operator.String(), + from: s.customer.String(), + to: s.vendor.String(), tokenID: collection.NewNFTID(s.nftClassID, s.numNFTs+1), err: collection.ErrTokenNotOwnedBy, }, + "invalid operator": { + contractID: s.contractID, + from: s.customer.String(), + to: s.vendor.String(), + tokenID: rootNFTID, + err: sdkerrors.ErrInvalidAddress, + }, + "invalid contract id": { + operator: s.operator.String(), + from: s.customer.String(), + to: s.vendor.String(), + tokenID: rootNFTID, + err: collection.ErrInvalidContractID, + }, + "invalid from": { + contractID: s.contractID, + operator: s.operator.String(), + to: s.vendor.String(), + tokenID: rootNFTID, + err: sdkerrors.ErrInvalidAddress, + }, + "invalid to": { + contractID: s.contractID, + operator: s.operator.String(), + from: s.customer.String(), + tokenID: rootNFTID, + err: sdkerrors.ErrInvalidAddress, + }, + "empty ids": { + contractID: s.contractID, + operator: s.operator.String(), + from: s.customer.String(), + to: s.vendor.String(), + err: collection.ErrEmptyField, + }, + "invalid id": { + contractID: s.contractID, + operator: s.operator.String(), + from: s.customer.String(), + to: s.vendor.String(), + tokenID: "null", + err: collection.ErrInvalidTokenID, + }, } for name, tc := range testCases { s.Run(name, func() { ctx, _ := s.ctx.CacheContext() - req := &collection.MsgOperatorSendNFT{ - ContractId: tc.contractID, - Operator: tc.operator.String(), - From: tc.from.String(), - To: s.vendor.String(), - TokenIds: []string{tc.tokenID}, + var req *collection.MsgOperatorSendNFT + if tc.tokenID == "" { + req = &collection.MsgOperatorSendNFT{ + ContractId: tc.contractID, + Operator: tc.operator, + From: tc.from, + To: tc.to, + } + } else { + req = &collection.MsgOperatorSendNFT{ + ContractId: tc.contractID, + Operator: tc.operator, + From: tc.from, + To: tc.to, + TokenIds: []string{tc.tokenID}, + } } res, err := s.msgServer.OperatorSendNFT(ctx, req) s.Require().ErrorIs(err, tc.err) @@ -180,17 +292,16 @@ func (s *KeeperTestSuite) TestMsgOperatorSendNFT() { func (s *KeeperTestSuite) TestMsgAuthorizeOperator() { testCases := map[string]struct { - isNegativeCase bool - req *collection.MsgAuthorizeOperator - events sdk.Events - expectedError error + contractID string + holder sdk.AccAddress + operator sdk.AccAddress + events sdk.Events + err error }{ "valid request": { - req: &collection.MsgAuthorizeOperator{ - ContractId: s.contractID, - Holder: s.customer.String(), - Operator: s.vendor.String(), - }, + contractID: s.contractID, + holder: s.customer, + operator: s.vendor, events: sdk.Events{sdk.Event{ Type: "lbm.collection.v1.EventAuthorizedOperator", Attributes: []abci.EventAttribute{ @@ -201,67 +312,79 @@ func (s *KeeperTestSuite) TestMsgAuthorizeOperator() { }}, }, "contract not found": { - isNegativeCase: true, - req: &collection.MsgAuthorizeOperator{ - ContractId: "deadbeef", - Holder: s.customer.String(), - Operator: s.vendor.String(), - }, - expectedError: collection.ErrContractNotExist, + contractID: "deadbeef", + holder: s.customer, + operator: s.vendor, + err: collection.ErrContractNotExist, }, "already approved": { - isNegativeCase: true, - req: &collection.MsgAuthorizeOperator{ - ContractId: s.contractID, - Holder: s.customer.String(), - Operator: s.operator.String(), - }, - expectedError: collection.ErrCollectionAlreadyApproved, + contractID: s.contractID, + holder: s.customer, + operator: s.operator, + err: collection.ErrCollectionAlreadyApproved, + }, + "invalid contract id": { + holder: s.customer, + operator: s.operator, + err: collection.ErrInvalidContractID, + }, + "invalid holder": { + contractID: s.contractID, + operator: s.operator, + err: sdkerrors.ErrInvalidAddress, + }, + "empty operator": { + contractID: s.contractID, + holder: s.customer, + err: sdkerrors.ErrInvalidAddress, }, } for name, tc := range testCases { s.Run(name, func() { // Arrange - s.Require().NoError(tc.req.ValidateBasic()) - holder, err := sdk.AccAddressFromBech32(tc.req.Holder) - s.Require().NoError(err) - operator, err := sdk.AccAddressFromBech32(tc.req.Operator) - s.Require().NoError(err) ctx, _ := s.ctx.CacheContext() - prevAuth, _ := s.keeper.GetAuthorization(ctx, tc.req.ContractId, holder, operator) + prevAuth, _ := s.keeper.GetAuthorization(ctx, tc.contractID, tc.holder, tc.operator) // Act - res, err := s.msgServer.AuthorizeOperator(ctx, tc.req) - if tc.isNegativeCase { - s.Require().ErrorIs(err, tc.expectedError) + req := &collection.MsgAuthorizeOperator{ + ContractId: tc.contractID, + Holder: tc.holder.String(), + Operator: tc.operator.String(), + } + res, err := s.msgServer.AuthorizeOperator(ctx, req) + if tc.err != nil { + s.Require().ErrorIs(err, tc.err) return } s.Require().NoError(err) s.Require().NotNil(res) s.Require().Equal(tc.events, ctx.EventManager().Events()) - curAuth, err := s.keeper.GetAuthorization(ctx, tc.req.ContractId, holder, operator) + curAuth, err := s.keeper.GetAuthorization(ctx, tc.contractID, tc.holder, tc.operator) s.Require().NoError(err) s.Require().Nil(prevAuth) - s.Require().Equal(tc.req.Holder, curAuth.Holder) - s.Require().Equal(tc.req.Operator, curAuth.Operator) + h, err := s.addressCodec.BytesToString(tc.holder.Bytes()) + s.Require().NoError(err) + s.Require().Equal(h, curAuth.Holder) + o, err := s.addressCodec.BytesToString(tc.operator.Bytes()) + s.Require().NoError(err) + s.Require().Equal(o, curAuth.Operator) }) } } func (s *KeeperTestSuite) TestMsgRevokeOperator() { testCases := map[string]struct { - isNegativeCase bool - req *collection.MsgRevokeOperator - events sdk.Events - expectedError error + contractID string + holder sdk.AccAddress + operator sdk.AccAddress + events sdk.Events + err error }{ "valid request": { - req: &collection.MsgRevokeOperator{ - ContractId: s.contractID, - Holder: s.customer.String(), - Operator: s.operator.String(), - }, + contractID: s.contractID, + holder: s.customer, + operator: s.operator, events: sdk.Events{sdk.Event{ Type: "lbm.collection.v1.EventRevokedOperator", Attributes: []abci.EventAttribute{ @@ -272,40 +395,49 @@ func (s *KeeperTestSuite) TestMsgRevokeOperator() { }}, }, "contract not found": { - isNegativeCase: true, - req: &collection.MsgRevokeOperator{ - ContractId: "deadbeef", - Holder: s.customer.String(), - Operator: s.operator.String(), - }, - expectedError: collection.ErrContractNotExist, + contractID: "deadbeef", + holder: s.customer, + operator: s.vendor, + err: collection.ErrContractNotExist, }, "no authorization": { - isNegativeCase: true, - req: &collection.MsgRevokeOperator{ - ContractId: s.contractID, - Holder: s.customer.String(), - Operator: s.vendor.String(), - }, - expectedError: collection.ErrCollectionNotApproved, + contractID: s.contractID, + holder: s.customer, + operator: s.vendor, + err: collection.ErrCollectionNotApproved, + }, + "invalid contract id": { + holder: s.customer, + operator: s.operator, + err: collection.ErrInvalidContractID, + }, + "invalid holder": { + contractID: s.contractID, + operator: s.operator, + err: sdkerrors.ErrInvalidAddress, + }, + "empty operator": { + contractID: s.contractID, + holder: s.customer, + err: sdkerrors.ErrInvalidAddress, }, } for name, tc := range testCases { s.Run(name, func() { // Arrange - s.Require().NoError(tc.req.ValidateBasic()) - holder, err := sdk.AccAddressFromBech32(tc.req.Holder) - s.Require().NoError(err) - operator, err := sdk.AccAddressFromBech32(tc.req.Operator) - s.Require().NoError(err) ctx, _ := s.ctx.CacheContext() - prevAuth, _ := s.keeper.GetAuthorization(ctx, tc.req.ContractId, holder, operator) + prevAuth, _ := s.keeper.GetAuthorization(ctx, tc.contractID, tc.holder, tc.operator) // Act - res, err := s.msgServer.RevokeOperator(ctx, tc.req) - if tc.isNegativeCase { - s.Require().ErrorIs(err, tc.expectedError) + req := &collection.MsgRevokeOperator{ + ContractId: tc.contractID, + Holder: tc.holder.String(), + Operator: tc.operator.String(), + } + res, err := s.msgServer.RevokeOperator(ctx, req) + if tc.err != nil { + s.Require().ErrorIs(err, tc.err) return } s.Require().NoError(err) @@ -313,9 +445,13 @@ func (s *KeeperTestSuite) TestMsgRevokeOperator() { s.Require().Equal(tc.events, ctx.EventManager().Events()) s.Require().NotNil(prevAuth) - s.Require().Equal(tc.req.Holder, prevAuth.Holder) - s.Require().Equal(tc.req.Operator, prevAuth.Operator) - curAuth, err := s.keeper.GetAuthorization(ctx, tc.req.ContractId, holder, operator) + h, err := s.addressCodec.BytesToString(tc.holder.Bytes()) + s.Require().NoError(err) + s.Require().Equal(h, prevAuth.Holder) + o, err := s.addressCodec.BytesToString(tc.operator.Bytes()) + s.Require().NoError(err) + s.Require().Equal(o, prevAuth.Operator) + curAuth, err := s.keeper.GetAuthorization(ctx, tc.contractID, tc.holder, tc.operator) s.Require().ErrorIs(err, collection.ErrCollectionNotApproved) s.Require().Nil(curAuth) }) @@ -326,6 +462,9 @@ func (s *KeeperTestSuite) TestMsgCreateContract() { expectedNewContractID := "3336b76f" testCases := map[string]struct { owner sdk.AccAddress + name string + uri string + meta string err error events sdk.Events }{ @@ -380,6 +519,33 @@ func (s *KeeperTestSuite) TestMsgCreateContract() { }, }, }, + "invalid owner": { + name: "tibetian fox", + uri: "file:///tibetian_fox.png", + meta: "Tibetian fox", + err: sdkerrors.ErrInvalidAddress, + }, + "long name": { + owner: s.vendor, + name: string(make([]rune, 21)), + uri: "file:///tibetian_fox.png", + meta: "Tibetian fox", + err: collection.ErrInvalidNameLength, + }, + "invalid base image uri": { + owner: s.vendor, + name: "tibetian fox", + uri: string(make([]rune, 1001)), + meta: "Tibetian fox", + err: collection.ErrInvalidBaseImgURILength, + }, + "invalid meta": { + owner: s.vendor, + name: "tibetian fox", + uri: "file:///tibetian_fox.png", + meta: string(make([]rune, 1001)), + err: collection.ErrInvalidMetaLength, + }, } for name, tc := range testCases { @@ -388,13 +554,16 @@ func (s *KeeperTestSuite) TestMsgCreateContract() { req := &collection.MsgCreateContract{ Owner: tc.owner.String(), + Name: tc.name, + Uri: tc.uri, + Meta: tc.meta, } res, err := s.msgServer.CreateContract(ctx, req) - s.Require().Equal(expectedNewContractID, res.ContractId) - s.Require().ErrorIs(err, tc.err) if tc.err != nil { + s.Require().ErrorIs(err, tc.err) return } + s.Require().Equal(expectedNewContractID, res.ContractId) s.Require().NotNil(res) s.Require().Equal(tc.events, ctx.EventManager().Events()) @@ -408,6 +577,8 @@ func (s *KeeperTestSuite) TestMsgIssueNFT() { testCases := map[string]struct { contractID string owner sdk.AccAddress + name string + meta string err error events sdk.Events }{ @@ -455,6 +626,32 @@ func (s *KeeperTestSuite) TestMsgIssueNFT() { owner: s.customer, err: collection.ErrTokenNoPermission, }, + "invalid contract id": { + owner: s.vendor, + name: "tibetian fox", + meta: "Tibetian Fox", + err: collection.ErrInvalidContractID, + }, + "invalid operator": { + contractID: s.contractID, + name: "tibetian fox", + meta: "Tibetian Fox", + err: sdkerrors.ErrInvalidAddress, + }, + "long name": { + contractID: s.contractID, + owner: s.vendor, + meta: "Tibetian Fox", + name: string(make([]rune, 21)), + err: collection.ErrInvalidNameLength, + }, + "invalid meta": { + contractID: s.contractID, + owner: s.vendor, + name: "tibetian fox", + meta: string(make([]rune, 1001)), + err: collection.ErrInvalidMetaLength, + }, } for name, tc := range testCases { @@ -464,6 +661,8 @@ func (s *KeeperTestSuite) TestMsgIssueNFT() { req := &collection.MsgIssueNFT{ ContractId: tc.contractID, Owner: tc.owner.String(), + Name: tc.name, + Meta: tc.meta, } res, err := s.msgServer.IssueNFT(ctx, req) s.Require().ErrorIs(err, tc.err) @@ -494,6 +693,7 @@ func (s *KeeperTestSuite) TestMsgMintNFT() { testCases := map[string]struct { contractID string from sdk.AccAddress + to sdk.AccAddress params []collection.MintNFTParam err error events sdk.Events @@ -501,6 +701,7 @@ func (s *KeeperTestSuite) TestMsgMintNFT() { "valid request": { contractID: s.contractID, from: s.vendor, + to: s.customer, params: params, events: sdk.Events{ sdk.Event{ @@ -517,23 +718,90 @@ func (s *KeeperTestSuite) TestMsgMintNFT() { "contract not found": { contractID: "deadbeef", from: s.vendor, + to: s.customer, params: params, err: collection.ErrContractNotExist, }, "no permission": { contractID: s.contractID, from: s.customer, + to: s.customer, params: params, err: collection.ErrTokenNoPermission, }, "no class of the token": { contractID: s.contractID, from: s.vendor, + to: s.customer, params: []collection.MintNFTParam{{ + Name: "tibetian fox", TokenType: "deadbeef", }}, err: collection.ErrTokenTypeNotExist, }, + "invalid contract id": { + from: s.vendor, + to: s.customer, + params: params, + err: collection.ErrInvalidContractID, + }, + "invalid operator": { + contractID: s.contractID, + to: s.customer, + params: params, + err: sdkerrors.ErrInvalidAddress, + }, + "invalid to": { + contractID: s.contractID, + from: s.vendor, + params: params, + err: sdkerrors.ErrInvalidAddress, + }, + "empty params": { + contractID: s.contractID, + from: s.vendor, + to: s.customer, + err: collection.ErrEmptyField, + }, + "param of invalid token type": { + contractID: s.contractID, + from: s.vendor, + to: s.customer, + params: []collection.MintNFTParam{{ + Name: "tibetian fox", + }}, + err: collection.ErrInvalidTokenType, + }, + "param of empty name": { + contractID: s.contractID, + from: s.vendor, + to: s.customer, + params: []collection.MintNFTParam{{ + TokenType: s.nftClassID, + }}, + err: collection.ErrInvalidTokenName, + }, + "param of too long name": { + contractID: s.contractID, + from: s.vendor, + to: s.customer, + params: []collection.MintNFTParam{{ + TokenType: s.nftClassID, + Name: string(make([]rune, 21)), + }}, + err: collection.ErrInvalidNameLength, + }, + "param of invalid meta": { + contractID: s.contractID, + from: s.vendor, + to: s.customer, + params: []collection.MintNFTParam{{ + TokenType: s.nftClassID, + Name: "tibetian fox", + Meta: string(make([]rune, 1001)), + }}, + err: collection.ErrInvalidMetaLength, + }, } for name, tc := range testCases { @@ -543,7 +811,7 @@ func (s *KeeperTestSuite) TestMsgMintNFT() { req := &collection.MsgMintNFT{ ContractId: tc.contractID, From: tc.from.String(), - To: s.customer.String(), + To: tc.to.String(), Params: tc.params, } res, err := s.msgServer.MintNFT(ctx, req) @@ -597,7 +865,7 @@ func (s *KeeperTestSuite) TestMsgBurnNFT() { tokenIDs: []string{rootNFTID}, err: collection.ErrTokenNoPermission, }, - "not found": { + "NFT not found": { contractID: s.contractID, from: s.vendor, tokenIDs: []string{ @@ -613,6 +881,27 @@ func (s *KeeperTestSuite) TestMsgBurnNFT() { }, err: collection.ErrTokenNotOwnedBy, }, + "invalid contract id": { + from: s.vendor, + tokenIDs: []string{rootNFTID}, + err: collection.ErrInvalidContractID, + }, + "invalid from": { + contractID: s.contractID, + tokenIDs: []string{rootNFTID}, + err: sdkerrors.ErrInvalidAddress, + }, + "empty ids": { + contractID: s.contractID, + from: s.vendor, + err: collection.ErrEmptyField, + }, + "invalid id": { + contractID: s.contractID, + from: s.vendor, + tokenIDs: []string{""}, + err: collection.ErrInvalidTokenID, + }, } for name, tc := range testCases { @@ -686,7 +975,7 @@ func (s *KeeperTestSuite) TestMsgOperatorBurnNFT() { tokenIDs: []string{rootNFTID}, err: collection.ErrTokenNoPermission, }, - "not found": { + "NFT not found": { contractID: s.contractID, operator: s.operator, from: s.customer, @@ -704,6 +993,37 @@ func (s *KeeperTestSuite) TestMsgOperatorBurnNFT() { }, err: collection.ErrTokenNotOwnedBy, }, + "invalid contract id": { + operator: s.operator, + from: s.customer, + tokenIDs: []string{rootNFTID}, + err: collection.ErrInvalidContractID, + }, + "invalid grantee": { + contractID: s.contractID, + from: s.customer, + tokenIDs: []string{rootNFTID}, + err: sdkerrors.ErrInvalidAddress, + }, + "invalid from": { + contractID: s.contractID, + operator: s.operator, + tokenIDs: []string{rootNFTID}, + err: sdkerrors.ErrInvalidAddress, + }, + "empty ids": { + contractID: s.contractID, + operator: s.operator, + from: s.customer, + err: collection.ErrEmptyField, + }, + "invalid id": { + contractID: s.contractID, + operator: s.operator, + from: s.customer, + tokenIDs: []string{""}, + err: collection.ErrInvalidTokenID, + }, } for name, tc := range testCases { @@ -730,36 +1050,75 @@ func (s *KeeperTestSuite) TestMsgOperatorBurnNFT() { func (s *KeeperTestSuite) TestMsgModify() { expectedTokenIndex := collection.NewNFTID(s.nftClassID, 1)[8:] - changes := []collection.Attribute{{ + validChanges := []collection.Attribute{{ Key: collection.AttributeKeyName.String(), Value: "test", }} testCases := map[string]struct { - contractID string operator sdk.AccAddress + contractID string tokenType string tokenIndex string + changes []collection.Attribute err error events sdk.Events }{ - "valid request": { + "valid request - Contract": { contractID: s.contractID, operator: s.vendor, + changes: validChanges, events: sdk.Events{ sdk.Event{ Type: "lbm.collection.v1.EventModifiedContract", Attributes: []abci.EventAttribute{ - {Key: "changes", Value: mustJSONMarshal(changes), Index: false}, + {Key: "changes", Value: mustJSONMarshal(validChanges), Index: false}, {Key: "contract_id", Value: w(s.contractID), Index: false}, {Key: "operator", Value: w(s.vendor.String()), Index: false}, }, }, }, }, + "valid request - TokenClass": { + contractID: s.contractID, + operator: s.vendor, + tokenType: s.nftClassID, + changes: validChanges, + events: sdk.Events{ + sdk.Event{ + Type: "lbm.collection.v1.EventModifiedTokenClass", + Attributes: []abci.EventAttribute{ + {Key: "changes", Value: mustJSONMarshal(validChanges), Index: false}, + {Key: "contract_id", Value: w(s.contractID), Index: false}, + {Key: "operator", Value: w(s.vendor.String()), Index: false}, + {Key: "token_type", Value: w(s.nftClassID), Index: false}, + {Key: "type_name", Value: w(proto.MessageName(&collection.NFTClass{})), Index: false}, + }, + }, + }, + }, + "valid request - NFT": { + contractID: s.contractID, + operator: s.vendor, + tokenType: s.nftClassID, + tokenIndex: s.issuedNFTs[s.vendor.String()][0].TokenId[8:], + changes: validChanges, + events: sdk.Events{ + sdk.Event{ + Type: "lbm.collection.v1.EventModifiedNFT", + Attributes: []abci.EventAttribute{ + {Key: "changes", Value: mustJSONMarshal(validChanges), Index: false}, + {Key: "contract_id", Value: w(s.contractID), Index: false}, + {Key: "operator", Value: w(s.vendor.String()), Index: false}, + {Key: "token_id", Value: w(s.issuedNFTs[s.vendor.String()][0].TokenId), Index: false}, + }, + }, + }, + }, "contract not found": { contractID: "deadbeef", operator: s.vendor, + changes: validChanges, err: collection.ErrContractNotExist, }, "no permission": { @@ -767,6 +1126,7 @@ func (s *KeeperTestSuite) TestMsgModify() { operator: s.customer, tokenType: s.nftClassID, tokenIndex: expectedTokenIndex, + changes: validChanges, err: collection.ErrTokenNoPermission, }, "nft not found": { @@ -774,14 +1134,58 @@ func (s *KeeperTestSuite) TestMsgModify() { operator: s.vendor, tokenType: s.nftClassID, tokenIndex: collection.NewNFTID(s.nftClassID, s.numNFTs*3+1)[8:], + changes: validChanges, err: collection.ErrTokenNotExist, }, "nft class not found": { contractID: s.contractID, operator: s.vendor, tokenType: "deadbeef", + changes: validChanges, err: collection.ErrTokenTypeNotExist, }, + "invalid contract id": { + operator: s.vendor, + changes: validChanges, + err: collection.ErrInvalidContractID, + }, + "invalid owner": { + contractID: s.contractID, + changes: validChanges, + err: sdkerrors.ErrInvalidAddress, + }, + "invalid key of change": { + contractID: s.contractID, + operator: s.vendor, + changes: []collection.Attribute{{Key: strings.ToUpper(collection.AttributeKeyName.String()), Value: "tt"}}, + err: collection.ErrInvalidChangesField, + }, + "invalid value of change": { + contractID: s.contractID, + operator: s.vendor, + changes: []collection.Attribute{{Key: collection.AttributeKeyName.String(), Value: string(make([]rune, 21))}}, + err: collection.ErrInvalidNameLength, + }, + "empty changes": { + contractID: s.contractID, + operator: s.vendor, + err: collection.ErrEmptyChanges, + }, + "too many changes": { + contractID: s.contractID, + operator: s.vendor, + changes: make([]collection.Attribute, 101), + err: collection.ErrInvalidChangesFieldCount, + }, + "duplicated changes": { + contractID: s.contractID, + operator: s.vendor, + changes: []collection.Attribute{ + {Key: collection.AttributeKeyBaseImgURI.String(), Value: "hello"}, + {Key: collection.AttributeKeyURI.String(), Value: "world"}, + }, + err: collection.ErrDuplicateChangesField, + }, } for name, tc := range testCases { @@ -792,7 +1196,7 @@ func (s *KeeperTestSuite) TestMsgModify() { Owner: tc.operator.String(), TokenType: tc.tokenType, TokenIndex: tc.tokenIndex, - Changes: changes, + Changes: tc.changes, } res, err := s.msgServer.Modify(ctx, req) s.Require().ErrorIs(err, tc.err) @@ -846,6 +1250,30 @@ func (s *KeeperTestSuite) TestMsgGrantPermission() { permission: collection.LegacyPermissionModify.String(), err: collection.ErrTokenNoPermission, }, + "invalid contract id": { + granter: s.vendor, + grantee: s.operator, + permission: collection.LegacyPermissionMint.String(), + err: collection.ErrInvalidContractID, + }, + "invalid from": { + contractID: s.contractID, + grantee: s.operator, + permission: collection.LegacyPermissionMint.String(), + err: sdkerrors.ErrInvalidAddress, + }, + "invalid to": { + contractID: s.contractID, + granter: s.vendor, + permission: collection.LegacyPermissionMint.String(), + err: sdkerrors.ErrInvalidAddress, + }, + "invalid permission": { + contractID: s.contractID, + granter: s.vendor, + grantee: s.operator, + err: collection.ErrInvalidPermission, + }, } for name, tc := range testCases { @@ -905,6 +1333,21 @@ func (s *KeeperTestSuite) TestMsgRevokePermission() { permission: collection.LegacyPermissionModify.String(), err: collection.ErrTokenNoPermission, }, + "invalid contract id": { + from: s.operator, + permission: collection.LegacyPermissionMint.String(), + err: collection.ErrInvalidContractID, + }, + "invalid from": { + contractID: s.contractID, + permission: collection.LegacyPermissionMint.String(), + err: sdkerrors.ErrInvalidAddress, + }, + "invalid permission": { + contractID: s.contractID, + from: s.operator, + err: collection.ErrInvalidPermission, + }, } for name, tc := range testCases { diff --git a/x/collection/keeper/send_test.go b/x/collection/keeper/send_test.go index 8a7c34aeb4..739a0dd86b 100644 --- a/x/collection/keeper/send_test.go +++ b/x/collection/keeper/send_test.go @@ -3,8 +3,6 @@ package keeper_test import ( "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/Finschia/finschia-sdk/x/collection" ) @@ -56,9 +54,9 @@ func (s *KeeperTestSuite) TestAuthorizeOperator() { s.Run(name, func() { ctx, _ := s.ctx.CacheContext() - fromAddr, err := sdk.AccAddressFromBech32(from) + fromAddr, err := s.addressCodec.StringToBytes(from) s.Require().NoError(err) - operatorAddr, err := sdk.AccAddressFromBech32(operator) + operatorAddr, err := s.addressCodec.StringToBytes(operator) s.Require().NoError(err) _, queryErr := s.keeper.GetAuthorization(ctx, s.contractID, fromAddr, operatorAddr) @@ -88,9 +86,9 @@ func (s *KeeperTestSuite) TestRevokeOperator() { s.Run(name, func() { ctx, _ := s.ctx.CacheContext() - fromAddr, err := sdk.AccAddressFromBech32(from) + fromAddr, err := s.addressCodec.StringToBytes(from) s.Require().NoError(err) - operatorAddr, err := sdk.AccAddressFromBech32(operator) + operatorAddr, err := s.addressCodec.StringToBytes(operator) s.Require().NoError(err) _, queryErr := s.keeper.GetAuthorization(ctx, s.contractID, fromAddr, operatorAddr) diff --git a/x/collection/module/module.go b/x/collection/module/module.go index d5a4d5e9e6..e9c4aad891 100644 --- a/x/collection/module/module.go +++ b/x/collection/module/module.go @@ -8,6 +8,7 @@ import ( "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" + "cosmossdk.io/core/address" "cosmossdk.io/core/appmodule" "cosmossdk.io/core/store" "cosmossdk.io/depinject" @@ -38,6 +39,7 @@ var ( // AppModuleBasic defines the basic application module used by the collection module. type AppModuleBasic struct { cdc codec.Codec + ac address.Codec } // Name returns the ModuleName @@ -60,13 +62,13 @@ func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { } // ValidateGenesis performs genesis state validation for the collection module. -func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error { +func (ab AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error { var data collection.GenesisState if err := cdc.UnmarshalJSON(bz, &data); err != nil { return fmt.Errorf("failed to unmarshal %s genesis state: %w", collection.ModuleName, err) } - return collection.ValidateGenesis(data) + return collection.ValidateGenesis(data, ab.ac) } // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the collection module. @@ -77,13 +79,13 @@ func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *r } // GetQueryCmd returns the cli query commands for this module -func (AppModuleBasic) GetQueryCmd() *cobra.Command { - return cli.NewQueryCmd() +func (ab AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.NewQueryCmd(ab.ac) } // GetTxCmd returns the transaction commands for this module -func (AppModuleBasic) GetTxCmd() *cobra.Command { - return cli.NewTxCmd() +func (ab AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.NewTxCmd(ab.ac) } // ____________________________________________________________________________ @@ -97,8 +99,9 @@ type AppModule struct { // NewAppModule creates a new AppModule object func NewAppModule(cdc codec.Codec, keeper keeper.Keeper) AppModule { + ac := cdc.InterfaceRegistry().SigningContext().AddressCodec() return AppModule{ - AppModuleBasic: AppModuleBasic{cdc}, + AppModuleBasic: AppModuleBasic{cdc, ac}, keeper: keeper, } } @@ -129,7 +132,7 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) { var genesisState collection.GenesisState cdc.MustUnmarshalJSON(data, &genesisState) - am.keeper.InitGenesis(ctx, &genesisState) + am.keeper.InitGenesis(ctx, &genesisState, am.ac) } // ExportGenesis returns the exported genesis state as raw bytes for the collection diff --git a/x/collection/msgs.go b/x/collection/msgs.go index 44c8a92c34..e1228d09b5 100644 --- a/x/collection/msgs.go +++ b/x/collection/msgs.go @@ -7,7 +7,6 @@ import ( "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) @@ -17,7 +16,7 @@ const ( nameLengthLimit = 20 uriLengthLimit = 1000 metaLengthLimit = 1000 - changesLimit = 100 + ChangesLimit = 100 ) var ( @@ -114,7 +113,7 @@ func validateID(id string, reg *regexp.Regexp) error { return nil } -func validateName(name string) error { +func ValidateName(name string) error { if err := validateStringSize(name, nameLengthLimit, "name"); err != nil { return ErrInvalidNameLength.Wrap(err.Error()) } @@ -122,7 +121,7 @@ func validateName(name string) error { return nil } -func validateURI(uri string) error { +func ValidateURI(uri string) error { if err := validateStringSize(uri, uriLengthLimit, "uri"); err != nil { return ErrInvalidBaseImgURILength.Wrap(err.Error()) } @@ -130,7 +129,7 @@ func validateURI(uri string) error { return nil } -func validateMeta(meta string) error { +func ValidateMeta(meta string) error { if err := validateStringSize(meta, metaLengthLimit, "meta"); err != nil { return ErrInvalidMetaLength.Wrap(err.Error()) } @@ -145,7 +144,7 @@ func validateStringSize(str string, limit int, name string) error { return nil } -func validateLegacyPermission(permission string) error { +func ValidateLegacyPermission(permission string) error { return ValidatePermission(Permission(LegacyPermissionFromString(permission))) } @@ -156,21 +155,21 @@ func ValidatePermission(permission Permission) error { return nil } -func validateContractChange(change Attribute) error { +func ValidateContractChange(change Attribute) error { validators := map[string]func(string) error{ - AttributeKeyName.String(): validateName, - AttributeKeyBaseImgURI.String(): validateURI, - AttributeKeyMeta.String(): validateMeta, - AttributeKeyURI.String(): validateURI, + AttributeKeyName.String(): ValidateName, + AttributeKeyBaseImgURI.String(): ValidateURI, + AttributeKeyMeta.String(): ValidateMeta, + AttributeKeyURI.String(): ValidateURI, } return validateChange(change, validators) } -func validateTokenClassChange(change Attribute) error { +func ValidateTokenClassChange(change Attribute) error { validators := map[string]func(string) error{ - AttributeKeyName.String(): validateName, - AttributeKeyMeta.String(): validateMeta, + AttributeKeyName.String(): ValidateName, + AttributeKeyMeta.String(): ValidateMeta, } return validateChange(change, validators) @@ -183,333 +182,3 @@ func validateChange(change Attribute, validators map[string]func(string) error) } return validator(change.Value) } - -// ValidateBasic implements Msg. -func (m MsgSendNFT) ValidateBasic() error { - if err := ValidateContractID(m.ContractId); err != nil { - return err - } - - if _, err := sdk.AccAddressFromBech32(m.From); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", m.From) - } - if _, err := sdk.AccAddressFromBech32(m.To); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid to address: %s", m.To) - } - - if len(m.TokenIds) == 0 { - return ErrEmptyField.Wrap("token ids cannot be empty") - } - for _, id := range m.TokenIds { - if err := ValidateTokenID(id); err != nil { - return err - } - } - - return nil -} - -// ValidateBasic implements Msg. -func (m MsgOperatorSendNFT) ValidateBasic() error { - if err := ValidateContractID(m.ContractId); err != nil { - return err - } - - if _, err := sdk.AccAddressFromBech32(m.Operator); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid operator address: %s", m.Operator) - } - if _, err := sdk.AccAddressFromBech32(m.From); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", m.From) - } - if _, err := sdk.AccAddressFromBech32(m.To); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid to address: %s", m.To) - } - - if len(m.TokenIds) == 0 { - return ErrEmptyField.Wrap("token ids cannot be empty") - } - for _, id := range m.TokenIds { - if err := ValidateTokenID(id); err != nil { - return err - } - } - - return nil -} - -// ValidateBasic implements Msg. -func (m MsgAuthorizeOperator) ValidateBasic() error { - if err := ValidateContractID(m.ContractId); err != nil { - return err - } - - if _, err := sdk.AccAddressFromBech32(m.Holder); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid holder address: %s", m.Holder) - } - if _, err := sdk.AccAddressFromBech32(m.Operator); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid operator address: %s", m.Operator) - } - - if m.Operator == m.Holder { - return ErrApproverProxySame - } - - return nil -} - -// ValidateBasic implements Msg. -func (m MsgRevokeOperator) ValidateBasic() error { - if err := ValidateContractID(m.ContractId); err != nil { - return err - } - - if _, err := sdk.AccAddressFromBech32(m.Holder); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid holder address: %s", m.Holder) - } - if _, err := sdk.AccAddressFromBech32(m.Operator); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid operator address: %s", m.Operator) - } - - if m.Operator == m.Holder { - return ErrApproverProxySame - } - - return nil -} - -// ValidateBasic implements Msg. -func (m MsgCreateContract) ValidateBasic() error { - if _, err := sdk.AccAddressFromBech32(m.Owner); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid owner address: %s", m.Owner) - } - - if err := validateName(m.Name); err != nil { - return err - } - - if err := validateURI(m.Uri); err != nil { - return err - } - - if err := validateMeta(m.Meta); err != nil { - return err - } - - return nil -} - -// ValidateBasic implements Msg. -func (m MsgIssueNFT) ValidateBasic() error { - if err := ValidateContractID(m.ContractId); err != nil { - return err - } - - if err := validateName(m.Name); err != nil { - return err - } - - if err := validateMeta(m.Meta); err != nil { - return err - } - - if _, err := sdk.AccAddressFromBech32(m.Owner); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid owner address: %s", m.Owner) - } - - return nil -} - -// ValidateBasic implements Msg. -func (m MsgMintNFT) ValidateBasic() error { - if err := ValidateContractID(m.ContractId); err != nil { - return err - } - - if _, err := sdk.AccAddressFromBech32(m.From); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", m.From) - } - if _, err := sdk.AccAddressFromBech32(m.To); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid to address: %s", m.To) - } - - if len(m.Params) == 0 { - return ErrEmptyField.Wrap("mint params cannot be empty") - } - for _, param := range m.Params { - classID := param.TokenType - if err := ValidateLegacyNFTClassID(classID); err != nil { - return err - } - - if len(param.Name) == 0 { - return ErrInvalidTokenName - } - if err := validateName(param.Name); err != nil { - return err - } - - if err := validateMeta(param.Meta); err != nil { - return err - } - } - - return nil -} - -// ValidateBasic implements Msg. -func (m MsgBurnNFT) ValidateBasic() error { - if err := ValidateContractID(m.ContractId); err != nil { - return err - } - - if _, err := sdk.AccAddressFromBech32(m.From); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", m.From) - } - - if len(m.TokenIds) == 0 { - return ErrEmptyField.Wrap("token ids cannot be empty") - } - for _, id := range m.TokenIds { - if err := ValidateLegacyNFTID(id); err != nil { - return err - } - } - - return nil -} - -// ValidateBasic implements Msg. -func (m MsgOperatorBurnNFT) ValidateBasic() error { - if err := ValidateContractID(m.ContractId); err != nil { - return err - } - - if _, err := sdk.AccAddressFromBech32(m.Operator); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid operator address: %s", m.Operator) - } - if _, err := sdk.AccAddressFromBech32(m.From); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", m.From) - } - - if len(m.TokenIds) == 0 { - return ErrEmptyField.Wrap("token ids cannot be empty") - } - for _, id := range m.TokenIds { - if err := ValidateLegacyNFTID(id); err != nil { - return err - } - } - - return nil -} - -func UpdateMsgModify(msg *MsgModify) { - for i, change := range msg.Changes { - key := change.Key - converted := AttrCanonicalKey(key) - if converted != key { - msg.Changes[i].Key = converted - } - } -} - -// ValidateBasic implements Msg. -func (m MsgModify) ValidateBasic() error { - UpdateMsgModify(&m) - - if err := ValidateContractID(m.ContractId); err != nil { - return err - } - - if _, err := sdk.AccAddressFromBech32(m.Owner); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid owner address: %s", m.Owner) - } - - if len(m.TokenType) != 0 { - classID := m.TokenType - if err := ValidateClassID(classID); err != nil { - return ErrInvalidTokenType.Wrap(err.Error()) - } - } - - if len(m.TokenIndex) != 0 { - tokenID := m.TokenType + m.TokenIndex - if err := ValidateTokenID(tokenID); err != nil { - return ErrInvalidTokenIndex.Wrap(err.Error()) - } - // reject modifying nft class with token index filled (daphne compat.) - if ValidateLegacyIdxNFT(tokenID) == nil { - return ErrInvalidTokenIndex.Wrap("cannot modify nft class with index filled") - } - } - - validator := validateTokenClassChange - if len(m.TokenType) == 0 { - if len(m.TokenIndex) == 0 { - validator = validateContractChange - } else { - return ErrTokenIndexWithoutType.Wrap("token index without type") - } - } - if len(m.Changes) == 0 { - return ErrEmptyChanges.Wrap("empty changes") - } - if len(m.Changes) > changesLimit { - return ErrInvalidChangesFieldCount.Wrapf("the number of changes exceeds the limit: %d > %d", len(m.Changes), changesLimit) - } - seenKeys := map[string]bool{} - for _, change := range m.Changes { - key := change.Key - if seenKeys[key] { - return ErrDuplicateChangesField.Wrapf("duplicate keys: %s", change.Key) - } - seenKeys[key] = true - - attribute := Attribute{ - Key: change.Key, - Value: change.Value, - } - if err := validator(attribute); err != nil { - return err - } - } - - return nil -} - -// ValidateBasic implements Msg. -func (m MsgGrantPermission) ValidateBasic() error { - if err := ValidateContractID(m.ContractId); err != nil { - return err - } - - if _, err := sdk.AccAddressFromBech32(m.From); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", m.From) - } - if _, err := sdk.AccAddressFromBech32(m.To); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid to address: %s", m.To) - } - - if err := validateLegacyPermission(m.Permission); err != nil { - return err - } - - return nil -} - -// ValidateBasic implements Msg. -func (m MsgRevokePermission) ValidateBasic() error { - if err := ValidateContractID(m.ContractId); err != nil { - return err - } - - if _, err := sdk.AccAddressFromBech32(m.From); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", m.From) - } - - if err := validateLegacyPermission(m.Permission); err != nil { - return err - } - - return nil -} diff --git a/x/collection/msgs_test.go b/x/collection/msgs_test.go index 17988117b3..eeb6bdb9e1 100644 --- a/x/collection/msgs_test.go +++ b/x/collection/msgs_test.go @@ -2,7 +2,6 @@ package collection_test import ( "fmt" - "strings" "testing" "github.com/stretchr/testify/require" @@ -10,854 +9,11 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" "github.com/Finschia/finschia-sdk/x/collection" ) -func TestMsgSendNFT(t *testing.T) { - addrs := make([]sdk.AccAddress, 2) - for i := range addrs { - addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - } - - ids := []string{collection.NewNFTID(contractID, 1)} - - testCases := map[string]struct { - contractID string - from sdk.AccAddress - to sdk.AccAddress - ids []string - err error - }{ - "valid msg": { - contractID: contractID, - from: addrs[0], - to: addrs[1], - ids: ids, - }, - "invalid from": { - contractID: contractID, - to: addrs[1], - ids: ids, - err: sdkerrors.ErrInvalidAddress, - }, - "invalid contract id": { - from: addrs[0], - to: addrs[1], - ids: ids, - err: collection.ErrInvalidContractID, - }, - "invalid to": { - contractID: contractID, - from: addrs[0], - ids: ids, - err: sdkerrors.ErrInvalidAddress, - }, - "empty token ids": { - contractID: contractID, - from: addrs[0], - to: addrs[1], - err: collection.ErrEmptyField, - }, - "invalid token ids": { - contractID: contractID, - from: addrs[0], - to: addrs[1], - ids: []string{""}, - err: collection.ErrInvalidTokenID, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - msg := collection.MsgSendNFT{ - ContractId: tc.contractID, - From: tc.from.String(), - To: tc.to.String(), - TokenIds: tc.ids, - } - - require.ErrorIs(t, msg.ValidateBasic(), tc.err) - if tc.err != nil { - return - } - }) - } -} - -func TestMsgOperatorSendNFT(t *testing.T) { - addrs := make([]sdk.AccAddress, 3) - for i := range addrs { - addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - } - - ids := []string{collection.NewNFTID(contractID, 1)} - - testCases := map[string]struct { - contractID string - operator sdk.AccAddress - from sdk.AccAddress - to sdk.AccAddress - ids []string - err error - }{ - "valid msg": { - contractID: contractID, - operator: addrs[0], - from: addrs[1], - to: addrs[2], - ids: ids, - }, - "invalid operator": { - contractID: contractID, - from: addrs[1], - to: addrs[2], - ids: ids, - err: sdkerrors.ErrInvalidAddress, - }, - "invalid contract id": { - operator: addrs[0], - from: addrs[1], - to: addrs[2], - ids: ids, - err: collection.ErrInvalidContractID, - }, - "invalid from": { - contractID: contractID, - operator: addrs[0], - to: addrs[1], - ids: ids, - err: sdkerrors.ErrInvalidAddress, - }, - "invalid to": { - contractID: contractID, - operator: addrs[0], - from: addrs[1], - ids: ids, - err: sdkerrors.ErrInvalidAddress, - }, - "empty ids": { - contractID: contractID, - operator: addrs[0], - from: addrs[1], - to: addrs[2], - err: collection.ErrEmptyField, - }, - "invalid id": { - contractID: contractID, - operator: addrs[0], - from: addrs[1], - to: addrs[2], - ids: []string{""}, - err: collection.ErrInvalidTokenID, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - msg := collection.MsgOperatorSendNFT{ - ContractId: tc.contractID, - Operator: tc.operator.String(), - From: tc.from.String(), - To: tc.to.String(), - TokenIds: tc.ids, - } - - require.ErrorIs(t, msg.ValidateBasic(), tc.err) - if tc.err != nil { - return - } - }) - } -} - -func TestMsgAuthorizeOperator(t *testing.T) { - addrs := make([]sdk.AccAddress, 2) - for i := range addrs { - addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - } - - testCases := map[string]struct { - contractID string - holder sdk.AccAddress - operator sdk.AccAddress - err error - }{ - "valid msg": { - contractID: contractID, - holder: addrs[0], - operator: addrs[1], - }, - "invalid contract id": { - holder: addrs[0], - operator: addrs[1], - err: collection.ErrInvalidContractID, - }, - "invalid holder": { - contractID: contractID, - operator: addrs[1], - err: sdkerrors.ErrInvalidAddress, - }, - "empty operator": { - contractID: contractID, - holder: addrs[0], - err: sdkerrors.ErrInvalidAddress, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - msg := collection.MsgAuthorizeOperator{ - ContractId: tc.contractID, - Holder: tc.holder.String(), - Operator: tc.operator.String(), - } - - require.ErrorIs(t, msg.ValidateBasic(), tc.err) - if tc.err != nil { - return - } - }) - } -} - -func TestMsgRevokeOperator(t *testing.T) { - addrs := make([]sdk.AccAddress, 2) - for i := range addrs { - addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - } - - testCases := map[string]struct { - contractID string - holder sdk.AccAddress - operator sdk.AccAddress - err error - }{ - "valid msg": { - contractID: contractID, - holder: addrs[0], - operator: addrs[1], - }, - "invalid contract id": { - holder: addrs[0], - operator: addrs[1], - err: collection.ErrInvalidContractID, - }, - "invalid holder": { - contractID: contractID, - operator: addrs[1], - err: sdkerrors.ErrInvalidAddress, - }, - "empty operator": { - contractID: contractID, - holder: addrs[0], - err: sdkerrors.ErrInvalidAddress, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - msg := collection.MsgRevokeOperator{ - ContractId: tc.contractID, - Holder: tc.holder.String(), - Operator: tc.operator.String(), - } - - require.ErrorIs(t, msg.ValidateBasic(), tc.err) - if tc.err != nil { - return - } - }) - } -} - -func TestMsgCreateContract(t *testing.T) { - addrs := make([]sdk.AccAddress, 1) - for i := range addrs { - addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - } - - const name = "tibetian fox" - uri := "file:///tibetian_fox.png" - meta := "Tibetian fox" - testCases := map[string]struct { - owner sdk.AccAddress - name string - baseImgURI string - meta string - err error - }{ - "valid msg": { - owner: addrs[0], - name: name, - baseImgURI: uri, - meta: meta, - }, - "invalid owner": { - name: name, - baseImgURI: uri, - meta: meta, - err: sdkerrors.ErrInvalidAddress, - }, - "long name": { - owner: addrs[0], - name: string(make([]rune, 21)), - baseImgURI: uri, - meta: meta, - err: collection.ErrInvalidNameLength, - }, - "invalid base image uri": { - owner: addrs[0], - name: name, - baseImgURI: string(make([]rune, 1001)), - meta: meta, - err: collection.ErrInvalidBaseImgURILength, - }, - "invalid meta": { - owner: addrs[0], - name: name, - baseImgURI: uri, - meta: string(make([]rune, 1001)), - err: collection.ErrInvalidMetaLength, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - msg := collection.MsgCreateContract{ - Owner: tc.owner.String(), - Name: tc.name, - Uri: tc.baseImgURI, - Meta: tc.meta, - } - - require.ErrorIs(t, msg.ValidateBasic(), tc.err) - if tc.err != nil { - return - } - }) - } -} - -func TestMsgIssueNFT(t *testing.T) { - addrs := make([]sdk.AccAddress, 1) - for i := range addrs { - addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - } - - contractID := contractID - name := "tibetian fox" - meta := "Tibetian Fox" - testCases := map[string]struct { - contractID string - operator sdk.AccAddress - name string - meta string - err error - }{ - "valid msg": { - contractID: contractID, - operator: addrs[0], - name: name, - meta: meta, - }, - "invalid contract id": { - operator: addrs[0], - name: name, - meta: meta, - err: collection.ErrInvalidContractID, - }, - "invalid operator": { - contractID: contractID, - name: name, - meta: meta, - err: sdkerrors.ErrInvalidAddress, - }, - "long name": { - contractID: contractID, - operator: addrs[0], - name: string(make([]rune, 21)), - meta: meta, - err: collection.ErrInvalidNameLength, - }, - "invalid meta": { - contractID: contractID, - operator: addrs[0], - name: name, - meta: string(make([]rune, 1001)), - err: collection.ErrInvalidMetaLength, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - msg := collection.MsgIssueNFT{ - ContractId: tc.contractID, - Owner: tc.operator.String(), - Name: tc.name, - Meta: tc.meta, - } - - require.ErrorIs(t, msg.ValidateBasic(), tc.err) - if tc.err != nil { - return - } - }) - } -} - -func TestMsgMintNFT(t *testing.T) { - addrs := make([]sdk.AccAddress, 2) - for i := range addrs { - addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - } - - params := []collection.MintNFTParam{{ - TokenType: contractID, - Name: "tibetian fox", - Meta: "Tibetian Fox", - }} - testCases := map[string]struct { - contractID string - operator sdk.AccAddress - to sdk.AccAddress - params []collection.MintNFTParam - err error - }{ - "valid msg": { - contractID: contractID, - operator: addrs[0], - to: addrs[1], - params: params, - }, - "invalid contract id": { - operator: addrs[0], - to: addrs[1], - params: params, - err: collection.ErrInvalidContractID, - }, - "invalid operator": { - contractID: contractID, - to: addrs[1], - params: params, - err: sdkerrors.ErrInvalidAddress, - }, - "invalid to": { - contractID: contractID, - operator: addrs[0], - params: params, - err: sdkerrors.ErrInvalidAddress, - }, - "empty params": { - contractID: contractID, - operator: addrs[0], - to: addrs[1], - err: collection.ErrEmptyField, - }, - "param of invalid token type": { - contractID: contractID, - operator: addrs[0], - to: addrs[1], - params: []collection.MintNFTParam{{ - Name: "tibetian fox", - }}, - err: collection.ErrInvalidTokenType, - }, - "param of empty name": { - contractID: contractID, - operator: addrs[0], - to: addrs[1], - params: []collection.MintNFTParam{{ - TokenType: contractID, - }}, - err: collection.ErrInvalidTokenName, - }, - "param of too long name": { - contractID: contractID, - operator: addrs[0], - to: addrs[1], - params: []collection.MintNFTParam{{ - TokenType: contractID, - Name: string(make([]rune, 21)), - }}, - err: collection.ErrInvalidNameLength, - }, - "param of invalid meta": { - contractID: contractID, - operator: addrs[0], - to: addrs[1], - params: []collection.MintNFTParam{{ - TokenType: contractID, - Name: "tibetian fox", - Meta: string(make([]rune, 1001)), - }}, - err: collection.ErrInvalidMetaLength, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - msg := collection.MsgMintNFT{ - ContractId: tc.contractID, - From: tc.operator.String(), - To: tc.to.String(), - Params: tc.params, - } - - require.ErrorIs(t, msg.ValidateBasic(), tc.err) - if tc.err != nil { - return - } - }) - } -} - -func TestMsgBurnNFT(t *testing.T) { - addrs := make([]sdk.AccAddress, 1) - for i := range addrs { - addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - } - - ids := []string{collection.NewNFTID(contractID, 1)} - - testCases := map[string]struct { - contractID string - from sdk.AccAddress - ids []string - err error - }{ - "valid msg": { - contractID: contractID, - from: addrs[0], - ids: ids, - }, - "invalid contract id": { - from: addrs[0], - ids: ids, - err: collection.ErrInvalidContractID, - }, - "invalid from": { - contractID: contractID, - ids: ids, - err: sdkerrors.ErrInvalidAddress, - }, - "empty ids": { - contractID: contractID, - from: addrs[0], - err: collection.ErrEmptyField, - }, - "invalid id": { - contractID: contractID, - from: addrs[0], - ids: []string{""}, - err: collection.ErrInvalidTokenID, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - msg := collection.MsgBurnNFT{ - ContractId: tc.contractID, - From: tc.from.String(), - TokenIds: tc.ids, - } - - require.ErrorIs(t, msg.ValidateBasic(), tc.err) - if tc.err != nil { - return - } - }) - } -} - -func TestMsgOperatorBurnNFT(t *testing.T) { - addrs := make([]sdk.AccAddress, 2) - for i := range addrs { - addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - } - - ids := []string{collection.NewNFTID(contractID, 1)} - - testCases := map[string]struct { - contractID string - grantee sdk.AccAddress - from sdk.AccAddress - ids []string - err error - }{ - "valid msg": { - contractID: contractID, - grantee: addrs[0], - from: addrs[1], - ids: ids, - }, - "invalid contract id": { - grantee: addrs[0], - from: addrs[1], - ids: ids, - err: collection.ErrInvalidContractID, - }, - "invalid grantee": { - contractID: contractID, - from: addrs[1], - ids: ids, - err: sdkerrors.ErrInvalidAddress, - }, - "invalid from": { - contractID: contractID, - grantee: addrs[0], - ids: ids, - err: sdkerrors.ErrInvalidAddress, - }, - "empty ids": { - contractID: contractID, - grantee: addrs[0], - from: addrs[1], - err: collection.ErrEmptyField, - }, - "invalid id": { - contractID: contractID, - grantee: addrs[0], - from: addrs[0], - ids: []string{""}, - err: collection.ErrInvalidTokenID, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - msg := collection.MsgOperatorBurnNFT{ - ContractId: tc.contractID, - Operator: tc.grantee.String(), - From: tc.from.String(), - TokenIds: tc.ids, - } - - require.ErrorIs(t, msg.ValidateBasic(), tc.err) - if tc.err != nil { - return - } - }) - } -} - -func TestMsgModify(t *testing.T) { - addrs := make([]sdk.AccAddress, 1) - for i := range addrs { - addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - } - - changes := []collection.Attribute{{Key: collection.AttributeKeyName.String(), Value: "New test"}} - testCases := map[string]struct { - contractID string - owner sdk.AccAddress - tokenType string - tokenIndex string - changes []collection.Attribute - err error - }{ - "valid contract modification": { - contractID: contractID, - owner: addrs[0], - changes: changes, - }, - "valid token class modification": { - contractID: contractID, - tokenType: contractID, - owner: addrs[0], - changes: changes, - }, - "invalid nft class modification": { - contractID: contractID, - tokenType: contractID, - tokenIndex: "00000000", - owner: addrs[0], - changes: changes, - err: collection.ErrInvalidTokenIndex, - }, - "valid nft modification": { - contractID: contractID, - tokenType: contractID, - tokenIndex: contractID, - owner: addrs[0], - changes: changes, - }, - "invalid contract id": { - owner: addrs[0], - changes: changes, - err: collection.ErrInvalidContractID, - }, - "invalid owner": { - contractID: contractID, - changes: changes, - err: sdkerrors.ErrInvalidAddress, - }, - "invalid key of change": { - contractID: contractID, - owner: addrs[0], - changes: []collection.Attribute{{Key: strings.ToUpper(collection.AttributeKeyName.String()), Value: "tt"}}, - err: collection.ErrInvalidChangesField, - }, - "invalid value of change": { - contractID: contractID, - owner: addrs[0], - changes: []collection.Attribute{{Key: collection.AttributeKeyName.String(), Value: string(make([]rune, 21))}}, - err: collection.ErrInvalidNameLength, - }, - "empty changes": { - contractID: contractID, - owner: addrs[0], - err: collection.ErrEmptyChanges, - }, - "too many changes": { - contractID: contractID, - owner: addrs[0], - changes: make([]collection.Attribute, 101), - err: collection.ErrInvalidChangesFieldCount, - }, - "duplicated changes": { - contractID: contractID, - owner: addrs[0], - changes: []collection.Attribute{ - {Key: collection.AttributeKeyBaseImgURI.String(), Value: "hello"}, - {Key: collection.AttributeKeyURI.String(), Value: "world"}, - }, - err: collection.ErrDuplicateChangesField, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - msg := collection.MsgModify{ - ContractId: tc.contractID, - TokenType: tc.tokenType, - TokenIndex: tc.tokenIndex, - Owner: tc.owner.String(), - Changes: tc.changes, - } - - require.ErrorIs(t, msg.ValidateBasic(), tc.err) - if tc.err != nil { - return - } - }) - } -} - -func TestMsgGrantPermission(t *testing.T) { - addrs := make([]sdk.AccAddress, 2) - for i := range addrs { - addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - } - - testCases := map[string]struct { - contractID string - from sdk.AccAddress - to sdk.AccAddress - permission string - err error - }{ - "valid msg": { - contractID: contractID, - from: addrs[0], - to: addrs[1], - permission: collection.LegacyPermissionMint.String(), - }, - "invalid contract id": { - from: addrs[0], - to: addrs[1], - permission: collection.LegacyPermissionMint.String(), - err: collection.ErrInvalidContractID, - }, - "invalid from": { - contractID: contractID, - to: addrs[1], - permission: collection.LegacyPermissionMint.String(), - err: sdkerrors.ErrInvalidAddress, - }, - "invalid to": { - contractID: contractID, - from: addrs[0], - permission: collection.LegacyPermissionMint.String(), - err: sdkerrors.ErrInvalidAddress, - }, - "invalid permission": { - contractID: contractID, - from: addrs[0], - to: addrs[1], - err: collection.ErrInvalidPermission, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - msg := collection.MsgGrantPermission{ - ContractId: tc.contractID, - From: tc.from.String(), - To: tc.to.String(), - Permission: tc.permission, - } - - require.ErrorIs(t, msg.ValidateBasic(), tc.err) - if tc.err != nil { - return - } - }) - } -} - -func TestMsgRevokePermission(t *testing.T) { - addrs := make([]sdk.AccAddress, 1) - for i := range addrs { - addrs[i] = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - } - - testCases := map[string]struct { - contractID string - from sdk.AccAddress - permission string - err error - }{ - "valid msg": { - contractID: contractID, - from: addrs[0], - permission: collection.LegacyPermissionMint.String(), - }, - "invalid contract id": { - from: addrs[0], - permission: collection.LegacyPermissionMint.String(), - err: collection.ErrInvalidContractID, - }, - "invalid from": { - contractID: contractID, - permission: collection.LegacyPermissionMint.String(), - err: sdkerrors.ErrInvalidAddress, - }, - "invalid permission": { - contractID: contractID, - from: addrs[0], - err: collection.ErrInvalidPermission, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - msg := collection.MsgRevokePermission{ - ContractId: tc.contractID, - From: tc.from.String(), - Permission: tc.permission, - } - - require.ErrorIs(t, msg.ValidateBasic(), tc.err) - if tc.err != nil { - return - } - }) - } -} - func TestAminoJSON(t *testing.T) { tx := legacytx.StdTx{} legacyAmino := codec.NewLegacyAmino()