diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e92859661..209c622739 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Breaking Changes * (proto) [\#923](https://github.com/line/lbm-sdk/pull/923) deprecate broadcast mode `block` +* (x/collection,token) [\#956](https://github.com/line/lbm-sdk/pull/956) Replace query errors on the original modules into gRPC ones ### Build, CI * (ci, build) [\#901](https://github.com/line/lbm-sdk/pull/901) Update release pipeline to match non-wasm env diff --git a/simapp/app.go b/simapp/app.go index d564f165b7..7ba8b69ba5 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -384,8 +384,8 @@ func NewSimApp( upgrade.NewAppModule(app.UpgradeKeeper), evidence.NewAppModule(app.EvidenceKeeper), params.NewAppModule(app.ParamsKeeper), - tokenmodule.NewAppModule(appCodec, app.TokenKeeper), - collectionmodule.NewAppModule(appCodec, app.CollectionKeeper), + tokenmodule.NewAppModule(appCodec, app.TokenKeeper, app.AccountKeeper), + collectionmodule.NewAppModule(appCodec, app.CollectionKeeper, app.AccountKeeper), authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), ) diff --git a/x/collection/client/testutil/query.go b/x/collection/client/testutil/query.go index 9ee7cd2d47..1fdbabdf3e 100644 --- a/x/collection/client/testutil/query.go +++ b/x/collection/client/testutil/query.go @@ -326,17 +326,17 @@ func (s *IntegrationTestSuite) TestNewQueryCmdNFTSupply() { "valid query": { []string{ s.contractID, - s.ftClassID, + s.nftClassID, }, true, &collection.QueryNFTSupplyResponse{ - Supply: s.balance.Mul(sdk.NewInt(4)), + Supply: sdk.NewInt(24), }, }, "extra args": { []string{ s.contractID, - s.ftClassID, + s.nftClassID, "extra", }, false, @@ -352,7 +352,7 @@ func (s *IntegrationTestSuite) TestNewQueryCmdNFTSupply() { "invalid contract id": { []string{ "", - s.ftClassID, + s.nftClassID, }, false, nil, @@ -401,17 +401,17 @@ func (s *IntegrationTestSuite) TestNewQueryCmdNFTMinted() { "valid query": { []string{ s.contractID, - s.ftClassID, + s.nftClassID, }, true, &collection.QueryNFTMintedResponse{ - Minted: s.balance.Mul(sdk.NewInt(5)), + Minted: sdk.NewInt(24), }, }, "extra args": { []string{ s.contractID, - s.ftClassID, + s.nftClassID, "extra", }, false, @@ -427,7 +427,7 @@ func (s *IntegrationTestSuite) TestNewQueryCmdNFTMinted() { "invalid contract id": { []string{ "", - s.ftClassID, + s.nftClassID, }, false, nil, @@ -476,17 +476,17 @@ func (s *IntegrationTestSuite) TestNewQueryCmdNFTBurnt() { "valid query": { []string{ s.contractID, - s.ftClassID, + s.nftClassID, }, true, &collection.QueryNFTBurntResponse{ - Burnt: s.balance, + Burnt: sdk.ZeroInt(), }, }, "extra args": { []string{ s.contractID, - s.ftClassID, + s.nftClassID, "extra", }, false, @@ -502,7 +502,7 @@ func (s *IntegrationTestSuite) TestNewQueryCmdNFTBurnt() { "invalid contract id": { []string{ "", - s.ftClassID, + s.nftClassID, }, false, nil, diff --git a/x/collection/expected_keepers.go b/x/collection/expected_keepers.go index 3de4c66e52..f39f7109c6 100644 --- a/x/collection/expected_keepers.go +++ b/x/collection/expected_keepers.go @@ -10,4 +10,8 @@ type ( NewID(ctx sdk.Context) string HasID(ctx sdk.Context, id string) bool } + + AuthKeeper interface { + HasAccount(sdk.Context, sdk.AccAddress) bool + } ) diff --git a/x/collection/keeper/grpc_query.go b/x/collection/keeper/grpc_query.go index c5132ff934..131fb14485 100644 --- a/x/collection/keeper/grpc_query.go +++ b/x/collection/keeper/grpc_query.go @@ -17,17 +17,61 @@ import ( ) type queryServer struct { - keeper Keeper + keeper Keeper + authKeeper collection.AuthKeeper } // NewQueryServer returns an implementation of the token QueryServer interface // for the provided Keeper. -func NewQueryServer(keeper Keeper) collection.QueryServer { +func NewQueryServer(keeper Keeper, authKeeper collection.AuthKeeper) collection.QueryServer { return &queryServer{ - keeper: keeper, + keeper: keeper, + authKeeper: authKeeper, } } +func (s queryServer) validateExistenceOfAccountGRPC(ctx sdk.Context, addr sdk.AccAddress) error { + if !s.authKeeper.HasAccount(ctx, addr) { + return status.Error(codes.NotFound, sdkerrors.ErrUnknownAddress.Wrap(addr.String()).Error()) + } + + return nil +} + +func (s queryServer) validateExistenceOfCollectionGRPC(ctx sdk.Context, id string) error { + if _, err := s.keeper.GetContract(ctx, id); err != nil { + return status.Error(codes.NotFound, err.Error()) + } + + return nil +} + +func (s queryServer) validateExistenceOfFTClassGRPC(ctx sdk.Context, contractID, classID string) error { + class, err := s.keeper.GetTokenClass(ctx, contractID, classID) + if err != nil { + return status.Error(codes.NotFound, err.Error()) + } + + _, ok := class.(*collection.FTClass) + if !ok { + return status.Error(codes.NotFound, sdkerrors.ErrInvalidType.Wrapf("not a class of fungible token: %s", classID).Error()) + } + return nil +} + +func (s queryServer) validateExistenceOfNFTClassGRPC(ctx sdk.Context, contractID, classID string) error { + class, err := s.keeper.GetTokenClass(ctx, contractID, classID) + if err != nil { + return status.Error(codes.NotFound, err.Error()) + } + + _, ok := class.(*collection.NFTClass) + if !ok { + return status.Error(codes.NotFound, sdkerrors.ErrInvalidType.Wrapf("not a class of non-fungible token: %s", classID).Error()) + } + return nil +} + var _ collection.QueryServer = queryServer{} // Balance queries the number of tokens of a given token id owned by the owner. @@ -50,6 +94,15 @@ func (s queryServer) Balance(c context.Context, req *collection.QueryBalanceRequ } ctx := sdk.UnwrapSDKContext(c) + + if err := s.validateExistenceOfAccountGRPC(ctx, addr); err != nil { + return nil, err + } + + if err := s.validateExistenceOfCollectionGRPC(ctx, req.ContractId); err != nil { + return nil, err + } + balance := s.keeper.GetBalance(ctx, req.ContractId, addr, req.TokenId) coin := collection.Coin{ TokenId: req.TokenId, @@ -113,9 +166,15 @@ func (s queryServer) FTSupply(c context.Context, req *collection.QueryFTSupplyRe classID := collection.SplitTokenID(req.TokenId) ctx := sdk.UnwrapSDKContext(c) - if _, err := s.keeper.GetTokenClass(ctx, req.ContractId, classID); err != nil { + + if err := s.validateExistenceOfCollectionGRPC(ctx, req.ContractId); err != nil { + return nil, err + } + + if err := s.validateExistenceOfFTClassGRPC(ctx, req.ContractId, classID); err != nil { return nil, err } + supply := s.keeper.GetSupply(ctx, req.ContractId, classID) return &collection.QueryFTSupplyResponse{Supply: supply}, nil @@ -137,9 +196,15 @@ func (s queryServer) FTMinted(c context.Context, req *collection.QueryFTMintedRe classID := collection.SplitTokenID(req.TokenId) ctx := sdk.UnwrapSDKContext(c) - if _, err := s.keeper.GetTokenClass(ctx, req.ContractId, classID); err != nil { + + if err := s.validateExistenceOfCollectionGRPC(ctx, req.ContractId); err != nil { + return nil, err + } + + if err := s.validateExistenceOfFTClassGRPC(ctx, req.ContractId, classID); err != nil { return nil, err } + minted := s.keeper.GetMinted(ctx, req.ContractId, classID) return &collection.QueryFTMintedResponse{Minted: minted}, nil @@ -161,9 +226,15 @@ func (s queryServer) FTBurnt(c context.Context, req *collection.QueryFTBurntRequ classID := collection.SplitTokenID(req.TokenId) ctx := sdk.UnwrapSDKContext(c) - if _, err := s.keeper.GetTokenClass(ctx, req.ContractId, classID); err != nil { + + if err := s.validateExistenceOfCollectionGRPC(ctx, req.ContractId); err != nil { + return nil, err + } + + if err := s.validateExistenceOfFTClassGRPC(ctx, req.ContractId, classID); err != nil { return nil, err } + burnt := s.keeper.GetBurnt(ctx, req.ContractId, classID) return &collection.QueryFTBurntResponse{Burnt: burnt}, nil @@ -184,9 +255,15 @@ func (s queryServer) NFTSupply(c context.Context, req *collection.QueryNFTSupply } ctx := sdk.UnwrapSDKContext(c) - if _, err := s.keeper.GetTokenClass(ctx, req.ContractId, classID); err != nil { + + if err := s.validateExistenceOfCollectionGRPC(ctx, req.ContractId); err != nil { + return nil, err + } + + if err := s.validateExistenceOfNFTClassGRPC(ctx, req.ContractId, classID); err != nil { return nil, err } + supply := s.keeper.GetSupply(ctx, req.ContractId, classID) return &collection.QueryNFTSupplyResponse{Supply: supply}, nil @@ -207,9 +284,15 @@ func (s queryServer) NFTMinted(c context.Context, req *collection.QueryNFTMinted } ctx := sdk.UnwrapSDKContext(c) - if _, err := s.keeper.GetTokenClass(ctx, req.ContractId, classID); err != nil { + + if err := s.validateExistenceOfCollectionGRPC(ctx, req.ContractId); err != nil { + return nil, err + } + + if err := s.validateExistenceOfNFTClassGRPC(ctx, req.ContractId, classID); err != nil { return nil, err } + minted := s.keeper.GetMinted(ctx, req.ContractId, classID) return &collection.QueryNFTMintedResponse{Minted: minted}, nil @@ -230,9 +313,15 @@ func (s queryServer) NFTBurnt(c context.Context, req *collection.QueryNFTBurntRe } ctx := sdk.UnwrapSDKContext(c) - if _, err := s.keeper.GetTokenClass(ctx, req.ContractId, classID); err != nil { + + if err := s.validateExistenceOfCollectionGRPC(ctx, req.ContractId); err != nil { return nil, err } + + if err := s.validateExistenceOfNFTClassGRPC(ctx, req.ContractId, classID); err != nil { + return nil, err + } + burnt := s.keeper.GetBurnt(ctx, req.ContractId, classID) return &collection.QueryNFTBurntResponse{Burnt: burnt}, nil @@ -250,7 +339,7 @@ func (s queryServer) Contract(c context.Context, req *collection.QueryContractRe ctx := sdk.UnwrapSDKContext(c) contract, err := s.keeper.GetContract(ctx, req.ContractId) if err != nil { - return nil, err + return nil, status.Error(codes.NotFound, err.Error()) } return &collection.QueryContractResponse{Contract: *contract}, nil @@ -271,9 +360,14 @@ func (s queryServer) TokenClassTypeName(c context.Context, req *collection.Query } ctx := sdk.UnwrapSDKContext(c) + + if err := s.validateExistenceOfCollectionGRPC(ctx, req.ContractId); err != nil { + return nil, err + } + class, err := s.keeper.GetTokenClass(ctx, req.ContractId, req.ClassId) if err != nil { - return nil, err + return nil, status.Error(codes.NotFound, err.Error()) } name := proto.MessageName(class) @@ -297,12 +391,12 @@ func (s queryServer) TokenType(c context.Context, req *collection.QueryTokenType ctx := sdk.UnwrapSDKContext(c) class, err := s.keeper.GetTokenClass(ctx, req.ContractId, classID) if err != nil { - return nil, err + return nil, status.Error(codes.NotFound, err.Error()) } nftClass, ok := class.(*collection.NFTClass) if !ok { - return nil, sdkerrors.ErrInvalidType.Wrapf("not a class of non-fungible token: %s", classID) + return nil, status.Error(codes.NotFound, sdkerrors.ErrInvalidType.Wrapf("not a class of non-fungible token: %s", classID).Error()) } tokenType := collection.TokenType{ @@ -372,7 +466,7 @@ func (s queryServer) Token(c context.Context, req *collection.QueryTokenRequest) ctx := sdk.UnwrapSDKContext(c) legacyToken, err := s.getToken(ctx, req.ContractId, req.TokenId) if err != nil { - return nil, err + return nil, status.Error(codes.NotFound, err.Error()) } any, err := codectypes.NewAnyWithValue(legacyToken) @@ -397,10 +491,15 @@ func (s queryServer) Root(c context.Context, req *collection.QueryRootRequest) ( } ctx := sdk.UnwrapSDKContext(c) - if err := s.keeper.hasNFT(ctx, req.ContractId, req.TokenId); err != nil { + + if err := s.validateExistenceOfCollectionGRPC(ctx, req.ContractId); err != nil { return nil, err } + if err := s.keeper.hasNFT(ctx, req.ContractId, req.TokenId); err != nil { + return nil, status.Error(codes.NotFound, err.Error()) + } + root := s.keeper.GetRoot(ctx, req.ContractId, req.TokenId) token, err := s.keeper.GetNFT(ctx, req.ContractId, root) if err != nil { @@ -424,10 +523,15 @@ func (s queryServer) Parent(c context.Context, req *collection.QueryParentReques } ctx := sdk.UnwrapSDKContext(c) - if err := s.keeper.hasNFT(ctx, req.ContractId, req.TokenId); err != nil { + + if err := s.validateExistenceOfCollectionGRPC(ctx, req.ContractId); err != nil { return nil, err } + if err := s.keeper.hasNFT(ctx, req.ContractId, req.TokenId); err != nil { + return nil, status.Error(codes.NotFound, err.Error()) + } + parent, err := s.keeper.GetParent(ctx, req.ContractId, req.TokenId) if err != nil { return nil, nil @@ -455,10 +559,15 @@ func (s queryServer) Children(c context.Context, req *collection.QueryChildrenRe } ctx := sdk.UnwrapSDKContext(c) - if err := s.keeper.hasNFT(ctx, req.ContractId, req.TokenId); err != nil { + + if err := s.validateExistenceOfCollectionGRPC(ctx, req.ContractId); err != nil { return nil, err } + if err := s.keeper.hasNFT(ctx, req.ContractId, req.TokenId); err != nil { + return nil, status.Error(codes.NotFound, err.Error()) + } + store := ctx.KVStore(s.keeper.storeKey) childStore := prefix.NewStore(store, childKeyPrefixByTokenID(req.ContractId, req.TokenId)) var children []collection.NFT @@ -494,6 +603,15 @@ func (s queryServer) GranteeGrants(c context.Context, req *collection.QueryGrant } ctx := sdk.UnwrapSDKContext(c) + + if err := s.validateExistenceOfAccountGRPC(ctx, granteeAddr); err != nil { + return nil, err + } + + if err := s.validateExistenceOfCollectionGRPC(ctx, req.ContractId); err != nil { + return nil, err + } + store := ctx.KVStore(s.keeper.storeKey) grantStore := prefix.NewStore(store, grantKeyPrefixByGrantee(req.ContractId, granteeAddr)) var grants []collection.Grant @@ -531,6 +649,18 @@ func (s queryServer) IsOperatorFor(c context.Context, req *collection.QueryIsOpe } ctx := sdk.UnwrapSDKContext(c) + + if err := s.validateExistenceOfAccountGRPC(ctx, operator); err != nil { + return nil, err + } + if err := s.validateExistenceOfAccountGRPC(ctx, holder); err != nil { + return nil, err + } + + if err := s.validateExistenceOfCollectionGRPC(ctx, req.ContractId); err != nil { + return nil, err + } + _, err = s.keeper.GetAuthorization(ctx, req.ContractId, holder, operator) authorized := (err == nil) diff --git a/x/collection/keeper/grpc_query_test.go b/x/collection/keeper/grpc_query_test.go index 8ce4b0381d..a5c6f25bc6 100644 --- a/x/collection/keeper/grpc_query_test.go +++ b/x/collection/keeper/grpc_query_test.go @@ -29,20 +29,20 @@ func (s *KeeperTestSuite) TestQueryBalance() { postTest: func(res *collection.QueryBalanceResponse) { expected := collection.Coin{ TokenId: tokenID, - Amount: s.balance, + Amount: s.balance, } s.Require().Equal(expected, res.Balance) }, }, "valid request with zero amount": { contractID: s.contractID, - address: s.vendor, - tokenID: "deadbeefdeadbeef", + address: s.stranger, + tokenID: tokenID, valid: true, postTest: func(res *collection.QueryBalanceResponse) { expected := collection.Coin{ - TokenId: "deadbeefdeadbeef", - Amount: sdk.ZeroInt(), + TokenId: tokenID, + Amount: sdk.ZeroInt(), } s.Require().Equal(expected, res.Balance) }, @@ -55,9 +55,19 @@ func (s *KeeperTestSuite) TestQueryBalance() { contractID: s.contractID, tokenID: tokenID, }, - "valid token id": { + "invalid token id": { + contractID: s.contractID, + address: s.vendor, + }, + "address not found": { contractID: s.contractID, + address: sdk.AccAddress("notfound"), + tokenID: tokenID, + }, + "collection not found": { + contractID: "deadbeef", address: s.vendor, + tokenID: tokenID, }, } @@ -166,10 +176,18 @@ func (s *KeeperTestSuite) TestQueryFTSupply() { "invalid token id": { contractID: s.contractID, }, - "no such a token": { + "collection not found": { + contractID: "deadbeef", + tokenID: tokenID, + }, + "token not found": { contractID: s.contractID, tokenID: collection.NewFTID("00bab10c"), }, + "not a class of ft": { + contractID: s.contractID, + tokenID: collection.NewFTID(s.nftClassID), + }, } for name, tc := range testCases { @@ -216,10 +234,18 @@ func (s *KeeperTestSuite) TestQueryFTMinted() { "invalid token id": { contractID: s.contractID, }, - "no such a token": { + "collection not found": { + contractID: "deadbeef", + tokenID: tokenID, + }, + "token not found": { contractID: s.contractID, tokenID: collection.NewFTID("00bab10c"), }, + "not a class of ft": { + contractID: s.contractID, + tokenID: collection.NewFTID(s.nftClassID), + }, } for name, tc := range testCases { @@ -266,10 +292,18 @@ func (s *KeeperTestSuite) TestQueryFTBurnt() { "invalid token id": { contractID: s.contractID, }, - "no such a token": { + "collection not found": { + contractID: "deadbeef", + tokenID: tokenID, + }, + "token not found": { contractID: s.contractID, tokenID: collection.NewFTID("00bab10c"), }, + "not a class of ft": { + contractID: s.contractID, + tokenID: collection.NewFTID(s.nftClassID), + }, } for name, tc := range testCases { @@ -315,10 +349,18 @@ func (s *KeeperTestSuite) TestQueryNFTSupply() { "invalid token type": { contractID: s.contractID, }, - "no such a token type": { + "collection not found": { + contractID: "deadbeef", + tokenType: s.nftClassID, + }, + "token type not found": { contractID: s.contractID, tokenType: "deadbeef", }, + "not a class of nft": { + contractID: s.contractID, + tokenType: s.ftClassID, + }, } for name, tc := range testCases { @@ -364,10 +406,18 @@ func (s *KeeperTestSuite) TestQueryNFTMinted() { "invalid token type": { contractID: s.contractID, }, - "no such a token type": { + "collection not found": { + contractID: "deadbeef", + tokenType: s.nftClassID, + }, + "token type not found": { contractID: s.contractID, tokenType: "deadbeef", }, + "not a class of nft": { + contractID: s.contractID, + tokenType: s.ftClassID, + }, } for name, tc := range testCases { @@ -413,10 +463,18 @@ func (s *KeeperTestSuite) TestQueryNFTBurnt() { "invalid token type": { contractID: s.contractID, }, - "no such a token type": { + "collection not found": { + contractID: "deadbeef", + tokenType: s.nftClassID, + }, + "token type not found": { contractID: s.contractID, tokenType: "deadbeef", }, + "not a class of nft": { + contractID: s.contractID, + tokenType: s.ftClassID, + }, } for name, tc := range testCases { @@ -502,7 +560,11 @@ func (s *KeeperTestSuite) TestQueryTokenClassTypeName() { "invalid class id": { contractID: s.contractID, }, - "no such a class": { + "collection not found": { + contractID: "deadbeef", + classID: s.ftClassID, + }, + "class not found": { contractID: s.contractID, classID: "00bab10c", }, @@ -552,7 +614,11 @@ func (s *KeeperTestSuite) TestQueryTokenType() { "invalid token type": { contractID: s.contractID, }, - "no such a token type": { + "collection not found": { + contractID: "deadbeef", + tokenType: s.nftClassID, + }, + "token type not found": { contractID: s.contractID, tokenType: "deadbeef", }, @@ -679,7 +745,11 @@ func (s *KeeperTestSuite) TestQueryRoot() { "invalid token id": { contractID: s.contractID, }, - "no such a token": { + "collection not found": { + contractID: "deadbeef", + tokenID: tokenID, + }, + "token not found": { contractID: s.contractID, tokenID: collection.NewNFTID("deadbeef", 1), }, @@ -738,7 +808,11 @@ func (s *KeeperTestSuite) TestQueryParent() { "invalid token id": { contractID: s.contractID, }, - "no such a token": { + "collection not found": { + contractID: "deadbeef", + tokenID: tokenID, + }, + "token not found": { contractID: s.contractID, tokenID: collection.NewNFTID("deadbeef", 1), }, @@ -799,6 +873,14 @@ func (s *KeeperTestSuite) TestQueryChildren() { "invalid token id": { contractID: s.contractID, }, + "collection not found": { + contractID: "deadbeef", + tokenID: tokenID, + }, + "token not found": { + contractID: s.contractID, + tokenID: collection.NewNFTID("deadbeef", 1), + }, } for name, tc := range testCases { @@ -849,6 +931,14 @@ func (s *KeeperTestSuite) TestQueryGranteeGrants() { "invalid grantee": { contractID: s.contractID, }, + "collection not found": { + contractID: "deadbeef", + grantee: s.vendor, + }, + "grantee not found": { + contractID: s.contractID, + grantee: sdk.AccAddress("notfound"), + }, } for name, tc := range testCases { @@ -902,6 +992,21 @@ func (s *KeeperTestSuite) TestQueryIsOperatorFor() { contractID: s.contractID, operator: s.operator, }, + "collection not found": { + contractID: "deadbeef", + operator: s.operator, + holder: s.vendor, + }, + "operator not found": { + contractID: s.contractID, + operator: sdk.AccAddress("notfound"), + holder: s.customer, + }, + "holder not found": { + contractID: s.contractID, + operator: s.operator, + holder: sdk.AccAddress("notfound"), + }, } for name, tc := range testCases { diff --git a/x/collection/keeper/keeper_test.go b/x/collection/keeper/keeper_test.go index 8fd829d868..248519197f 100644 --- a/x/collection/keeper/keeper_test.go +++ b/x/collection/keeper/keeper_test.go @@ -64,7 +64,7 @@ func (s *KeeperTestSuite) SetupTest() { s.goCtx = sdk.WrapSDKContext(s.ctx) s.keeper = app.CollectionKeeper - s.queryServer = keeper.NewQueryServer(s.keeper) + s.queryServer = keeper.NewQueryServer(s.keeper, app.AccountKeeper) s.msgServer = keeper.NewMsgServer(s.keeper) s.depthLimit = 4 @@ -81,6 +81,10 @@ func (s *KeeperTestSuite) SetupTest() { } for i, address := range createRandomAccounts(len(addresses)) { *addresses[i] = address + + // create account + acc := app.AccountKeeper.NewAccountWithAddress(s.ctx, address) + app.AccountKeeper.SetAccount(s.ctx, acc) } s.balance = sdk.NewInt(1000000) diff --git a/x/collection/module/module.go b/x/collection/module/module.go index 65f7edad02..e2bb57dcf7 100644 --- a/x/collection/module/module.go +++ b/x/collection/module/module.go @@ -15,7 +15,6 @@ import ( sdk "github.com/line/lbm-sdk/types" "github.com/line/lbm-sdk/types/module" "github.com/line/lbm-sdk/x/collection" - "github.com/line/lbm-sdk/x/collection/client/cli" "github.com/line/lbm-sdk/x/collection/keeper" ) @@ -79,13 +78,15 @@ func (b AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry type AppModule struct { AppModuleBasic - keeper keeper.Keeper + keeper keeper.Keeper + authKeeper collection.AuthKeeper } // NewAppModule creates a new AppModule object -func NewAppModule(cdc codec.Codec, keeper keeper.Keeper) AppModule { +func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, authKeeper collection.AuthKeeper) AppModule { return AppModule{ - keeper: keeper, + keeper: keeper, + authKeeper: authKeeper, } } @@ -107,7 +108,7 @@ func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sd // module-specific GRPC queries. func (am AppModule) RegisterServices(cfg module.Configurator) { collection.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServer(am.keeper)) - collection.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServer(am.keeper)) + collection.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServer(am.keeper, am.authKeeper)) // m := keeper.NewMigrator(am.keeper) // migrations := map[uint64]func(sdk.Context) error{} diff --git a/x/token/expected_keepers.go b/x/token/expected_keepers.go index bc85027126..295e6d8f9e 100644 --- a/x/token/expected_keepers.go +++ b/x/token/expected_keepers.go @@ -13,4 +13,8 @@ type ( InitGenesis(ctx sdk.Context, data *ClassGenesisState) ExportGenesis(ctx sdk.Context) *ClassGenesisState } + + AuthKeeper interface { + HasAccount(ctx sdk.Context, addr sdk.AccAddress) bool + } ) diff --git a/x/token/keeper/grpc_query.go b/x/token/keeper/grpc_query.go index a8a8277a6e..ab58a09863 100644 --- a/x/token/keeper/grpc_query.go +++ b/x/token/keeper/grpc_query.go @@ -14,19 +14,37 @@ import ( ) type queryServer struct { - keeper Keeper + keeper Keeper + authKeeper token.AuthKeeper } // NewQueryServer returns an implementation of the token QueryServer interface // for the provided Keeper. -func NewQueryServer(keeper Keeper) token.QueryServer { +func NewQueryServer(keeper Keeper, authKeeper token.AuthKeeper) token.QueryServer { return &queryServer{ - keeper: keeper, + keeper: keeper, + authKeeper: authKeeper, } } var _ token.QueryServer = queryServer{} +func (s queryServer) validateExistenceOfAccountGRPC(ctx sdk.Context, addr sdk.AccAddress) error { + if !s.authKeeper.HasAccount(ctx, addr) { + return status.Error(codes.NotFound, sdkerrors.ErrUnknownAddress.Wrap(addr.String()).Error()) + } + + return nil +} + +func (s queryServer) validateExistenceOfClassGRPC(ctx sdk.Context, id string) error { + if _, err := s.keeper.GetClass(ctx, id); err != nil { + return status.Error(codes.NotFound, err.Error()) + } + + return nil +} + // Balance queries the number of tokens of a given class owned by the owner. func (s queryServer) Balance(c context.Context, req *token.QueryBalanceRequest) (*token.QueryBalanceResponse, error) { if req == nil { @@ -42,6 +60,11 @@ func (s queryServer) Balance(c context.Context, req *token.QueryBalanceRequest) } ctx := sdk.UnwrapSDKContext(c) + + if err := s.validateExistenceOfAccountGRPC(ctx, addr); err != nil { + return nil, err + } + balance := s.keeper.GetBalance(ctx, req.ContractId, addr) return &token.QueryBalanceResponse{Amount: balance}, nil @@ -58,10 +81,11 @@ func (s queryServer) Supply(c context.Context, req *token.QuerySupplyRequest) (* } ctx := sdk.UnwrapSDKContext(c) - // daphne compat. - if _, err := s.keeper.GetClass(ctx, req.ContractId); err != nil { + + if err := s.validateExistenceOfClassGRPC(ctx, req.ContractId); err != nil { return nil, err } + supply := s.keeper.GetSupply(ctx, req.ContractId) return &token.QuerySupplyResponse{Amount: supply}, nil @@ -78,10 +102,11 @@ func (s queryServer) Minted(c context.Context, req *token.QueryMintedRequest) (* } ctx := sdk.UnwrapSDKContext(c) - // daphne compat. - if _, err := s.keeper.GetClass(ctx, req.ContractId); err != nil { + + if err := s.validateExistenceOfClassGRPC(ctx, req.ContractId); err != nil { return nil, err } + minted := s.keeper.GetMinted(ctx, req.ContractId) return &token.QueryMintedResponse{Amount: minted}, nil @@ -98,10 +123,11 @@ func (s queryServer) Burnt(c context.Context, req *token.QueryBurntRequest) (*to } ctx := sdk.UnwrapSDKContext(c) - // daphne compat. - if _, err := s.keeper.GetClass(ctx, req.ContractId); err != nil { + + if err := s.validateExistenceOfClassGRPC(ctx, req.ContractId); err != nil { return nil, err } + burnt := s.keeper.GetBurnt(ctx, req.ContractId) return &token.QueryBurntResponse{Amount: burnt}, nil @@ -120,7 +146,7 @@ func (s queryServer) Contract(c context.Context, req *token.QueryContractRequest ctx := sdk.UnwrapSDKContext(c) class, err := s.keeper.GetClass(ctx, req.ContractId) if err != nil { - return nil, err + return nil, status.Error(codes.NotFound, err.Error()) } return &token.QueryContractResponse{Contract: *class}, nil @@ -140,6 +166,15 @@ func (s queryServer) GranteeGrants(c context.Context, req *token.QueryGranteeGra } ctx := sdk.UnwrapSDKContext(c) + + if err := s.validateExistenceOfAccountGRPC(ctx, grantee); err != nil { + return nil, err + } + + if err := s.validateExistenceOfClassGRPC(ctx, req.ContractId); err != nil { + return nil, err + } + store := ctx.KVStore(s.keeper.storeKey) grantStore := prefix.NewStore(store, grantKeyPrefixByGrantee(req.ContractId, grantee)) var grants []token.Grant @@ -176,6 +211,18 @@ func (s queryServer) IsOperatorFor(c context.Context, req *token.QueryIsOperator } ctx := sdk.UnwrapSDKContext(c) + + if err := s.validateExistenceOfAccountGRPC(ctx, operator); err != nil { + return nil, err + } + if err := s.validateExistenceOfAccountGRPC(ctx, holder); err != nil { + return nil, err + } + + if err := s.validateExistenceOfClassGRPC(ctx, req.ContractId); err != nil { + return nil, err + } + _, err = s.keeper.GetAuthorization(ctx, req.ContractId, holder, operator) authorized := err == nil diff --git a/x/token/keeper/grpc_query_test.go b/x/token/keeper/grpc_query_test.go index f079d74a2f..d75ff371cb 100644 --- a/x/token/keeper/grpc_query_test.go +++ b/x/token/keeper/grpc_query_test.go @@ -31,6 +31,10 @@ func (s *KeeperTestSuite) TestQueryBalance() { "invalid address": { contractID: s.contractID, }, + "address not found": { + contractID: s.contractID, + address: sdk.AccAddress("notfound"), + }, } for name, tc := range testCases { @@ -237,6 +241,14 @@ func (s *KeeperTestSuite) TestQueryGranteeGrants() { "invalid grantee": { contractID: s.contractID, }, + "class not found": { + contractID: "fee1dead", + grantee: s.vendor, + }, + "grantee not found": { + contractID: s.contractID, + grantee: sdk.AccAddress("notfound"), + }, } for name, tc := range testCases { @@ -290,6 +302,21 @@ func (s *KeeperTestSuite) TestQueryIsOperatorFor() { contractID: s.contractID, operator: s.operator, }, + "class not found": { + contractID: "fee1dead", + operator: s.operator, + holder: s.vendor, + }, + "operator not found": { + contractID: s.contractID, + operator: sdk.AccAddress("notfound"), + holder: s.customer, + }, + "holder not found": { + contractID: s.contractID, + operator: s.operator, + holder: sdk.AccAddress("notfound"), + }, } for name, tc := range testCases { diff --git a/x/token/keeper/keeper_test.go b/x/token/keeper/keeper_test.go index 02a610b3ea..4136ef48fe 100644 --- a/x/token/keeper/keeper_test.go +++ b/x/token/keeper/keeper_test.go @@ -57,7 +57,7 @@ func (s *KeeperTestSuite) SetupTest() { s.goCtx = sdk.WrapSDKContext(s.ctx) s.keeper = app.TokenKeeper - s.queryServer = keeper.NewQueryServer(s.keeper) + s.queryServer = keeper.NewQueryServer(s.keeper, app.AccountKeeper) s.msgServer = keeper.NewMsgServer(s.keeper) addresses := []*sdk.AccAddress{ @@ -68,6 +68,9 @@ func (s *KeeperTestSuite) SetupTest() { } for i, address := range createRandomAccounts(len(addresses)) { *addresses[i] = address + + acc := app.AccountKeeper.NewAccountWithAddress(s.ctx, address) + app.AccountKeeper.SetAccount(s.ctx, acc) } s.balance = sdk.NewInt(1000) diff --git a/x/token/module/module.go b/x/token/module/module.go index 8740819d55..daf0346e1c 100644 --- a/x/token/module/module.go +++ b/x/token/module/module.go @@ -78,13 +78,15 @@ func (b AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry type AppModule struct { AppModuleBasic - keeper keeper.Keeper + keeper keeper.Keeper + authKeeper token.AuthKeeper } // NewAppModule creates a new AppModule object -func NewAppModule(cdc codec.Codec, keeper keeper.Keeper) AppModule { +func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, authKeeper token.AuthKeeper) AppModule { return AppModule{ - keeper: keeper, + keeper: keeper, + authKeeper: authKeeper, } } @@ -106,7 +108,7 @@ func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sd // module-specific GRPC queries. func (am AppModule) RegisterServices(cfg module.Configurator) { token.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServer(am.keeper)) - token.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServer(am.keeper)) + token.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServer(am.keeper, am.authKeeper)) // m := keeper.NewMigrator(am.keeper) // migrations := map[uint64]func(sdk.Context) error{}