From d2e0d22cdba0d02fb8f3cd5c8acabde93cc335db Mon Sep 17 00:00:00 2001 From: HYChoi Date: Mon, 28 Jun 2021 09:08:32 +0100 Subject: [PATCH 1/2] Allow to paginate user blocks See PR #506 --- CHANGELOG.md | 1 + .../queries/profiles/user-blocks.md | 2 +- go.mod | 2 + go.sum | 4 +- .../v1beta1/query_relationships.proto | 3 + x/profiles/client/cli/cli_relationships.go | 20 +- .../client/cli/cli_relationships_test.go | 8 + x/profiles/keeper/alias_functions.go | 17 ++ x/profiles/keeper/grpc_query.go | 26 ++- x/profiles/keeper/grpc_query_test.go | 71 ++++++ x/profiles/keeper/keeper_blocks.go | 57 +++-- x/profiles/simulation/decoder_test.go | 2 +- x/profiles/types/keys.go | 16 +- x/profiles/types/models_relationships.go | 7 + x/profiles/types/query.pb.gw.go | 18 ++ x/profiles/types/query_relationships.pb.go | 220 +++++++++++++++--- 16 files changed, 399 insertions(+), 75 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e39b4820fe..f4abfe7f62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Updated Cosmos SDK to `v0.42.6` ([#509](https://github.com/desmos-labs/desmos/issues/509)) - Added the possibility to verify a profile with an external application ([#472](https://github.com/desmos-labs/desmos/issues/472)) - Improved the posts query ([#499](https://github.com/desmos-labs/desmos/issues/499)) +- Added the ability to paginate user blocks ([#495](https://github.com/desmos-labs/desmos/issues/495)) - Added the post comments query ([#510](https://github.com/desmos-labs/desmos/pull/510)) - Added the ability to paginate incoming DTag transfer requests ([#519](https://github.com/desmos-labs/desmos/pull/519)) diff --git a/docs/developers/queries/profiles/user-blocks.md b/docs/developers/queries/profiles/user-blocks.md index d007210401..761213c05f 100644 --- a/docs/developers/queries/profiles/user-blocks.md +++ b/docs/developers/queries/profiles/user-blocks.md @@ -3,7 +3,7 @@ This query allows you to retrieve the user blocked by the user with the given `a **CLI** ```bash -desmos query profiles blocklist [address] +desmos query profiles blocklist [address] [[subspace]] # Example # desmos query profiles blocklist desmos13p5pamrljhza3fp4es5m3llgmnde5fzcpq6nud diff --git a/go.mod b/go.mod index 5f5efc37cd..a235a10476 100644 --- a/go.mod +++ b/go.mod @@ -30,3 +30,5 @@ replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alp replace github.com/cosmos/cosmos-sdk => github.com/desmos-labs/cosmos-sdk v0.42.5-0.20210622061628-2057c75ad60d replace google.golang.org/grpc => google.golang.org/grpc v1.33.2 + +replace github.com/keybase/go-keychain => github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 diff --git a/go.sum b/go.sum index 43aaae5db1..5ca9348c9d 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,8 @@ cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqCl cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= github.com/99designs/keyring v1.1.6 h1:kVDC2uCgVwecxCk+9zoCt2uEL6dt+dfVzMvGgnVcIuM= github.com/99designs/keyring v1.1.6/go.mod h1:16e0ds7LGQQcT59QqkTg72Hh5ShM51Byv5PEmW6uoRU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -327,8 +329,6 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM= -github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= diff --git a/proto/desmos/profiles/v1beta1/query_relationships.proto b/proto/desmos/profiles/v1beta1/query_relationships.proto index 614885e4a0..1138da44e7 100644 --- a/proto/desmos/profiles/v1beta1/query_relationships.proto +++ b/proto/desmos/profiles/v1beta1/query_relationships.proto @@ -50,6 +50,8 @@ message QueryUserBlocksRequest { // address of the user to query the blocks for string user = 1; + string subspace_id = 2; + cosmos.base.query.v1beta1.PageRequest pagination = 3; } // QueryUserBlocksResponse is the response type for the Query/UserBlocks RPC @@ -58,4 +60,5 @@ message QueryUserBlocksResponse { // blocks represent the list of all the blocks for the queried user repeated desmos.profiles.v1beta1.UserBlock blocks = 1 [ (gogoproto.nullable) = false ]; + cosmos.base.query.v1beta1.PageResponse pagination = 2; } \ No newline at end of file diff --git a/x/profiles/client/cli/cli_relationships.go b/x/profiles/client/cli/cli_relationships.go index bd674ac6a7..f716de0231 100644 --- a/x/profiles/client/cli/cli_relationships.go +++ b/x/profiles/client/cli/cli_relationships.go @@ -165,12 +165,12 @@ func GetCmdQueryUserRelationships() *cobra.Command { return cmd } -// GetCmdQueryUserBlocks returns the command allowing to query all the blocks of a single user +// GetCmdQueryUserBlocks returns the command allowing to query all the blocks of a single user with optional subspace func GetCmdQueryUserBlocks() *cobra.Command { cmd := &cobra.Command{ - Use: "blocklist [address]", + Use: "blocklist [address] [[subspace-id]] ", Short: "Retrieve the list of all the blocked users of the given address", - Args: cobra.ExactArgs(1), + Args: cobra.RangeArgs(1, 2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { @@ -178,9 +178,20 @@ func GetCmdQueryUserBlocks() *cobra.Command { } queryClient := types.NewQueryClient(clientCtx) + user := args[0] + var subspace string + if len(args) == 2 { + subspace = args[1] + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + res, err := queryClient.UserBlocks( context.Background(), - &types.QueryUserBlocksRequest{User: args[0]}) + &types.QueryUserBlocksRequest{User: user, SubspaceId: subspace, Pagination: pageReq}) if err != nil { return err } @@ -190,6 +201,7 @@ func GetCmdQueryUserBlocks() *cobra.Command { } flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, types.QueryUserBlocks) return cmd } diff --git a/x/profiles/client/cli/cli_relationships_test.go b/x/profiles/client/cli/cli_relationships_test.go index 9185c751b9..ac39b9c02c 100644 --- a/x/profiles/client/cli/cli_relationships_test.go +++ b/x/profiles/client/cli/cli_relationships_test.go @@ -102,6 +102,10 @@ func (s *IntegrationTestSuite) TestCmdQueryUserBlocks() { expectErr: false, expectedOutput: types.QueryUserBlocksResponse{ Blocks: []types.UserBlock{}, + Pagination: &query.PageResponse{ + NextKey: nil, + Total: 0, + }, }, }, { @@ -120,6 +124,10 @@ func (s *IntegrationTestSuite) TestCmdQueryUserBlocks() { "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", ), }, + Pagination: &query.PageResponse{ + NextKey: nil, + Total: 0, + }, }, }, } diff --git a/x/profiles/keeper/alias_functions.go b/x/profiles/keeper/alias_functions.go index f6147a8356..544defbc8a 100644 --- a/x/profiles/keeper/alias_functions.go +++ b/x/profiles/keeper/alias_functions.go @@ -101,6 +101,23 @@ func (k Keeper) IterateUserRelationships(ctx sdk.Context, user string, fn func(i } } +// IterateBlockedUsers iterates through the list of users blocked by the specified user and performs the given function +func (k Keeper) IterateBlockedUsers(ctx sdk.Context, user string, fn func(index int64, blocks types.UserBlock) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.BlockerPrefix(user)) + defer iterator.Close() + + i := int64(0) + for ; iterator.Valid(); iterator.Next() { + block := types.MustUnmarshalUserBlock(k.cdc, iterator.Value()) + stop := fn(i, block) + if stop { + break + } + i++ + } +} + // -------------------------------------------------------------------------------------------------------------------- // IterateUserApplicationLinks iterates through all the application links realted to the given user diff --git a/x/profiles/keeper/grpc_query.go b/x/profiles/keeper/grpc_query.go index a1e3086a52..b1e0b3a353 100644 --- a/x/profiles/keeper/grpc_query.go +++ b/x/profiles/keeper/grpc_query.go @@ -123,8 +123,30 @@ func (k Keeper) UserRelationships(ctx context.Context, request *types.QueryUserR // UserBlocks implements the Query/UserBlocks gRPC method func (k Keeper) UserBlocks(ctx context.Context, request *types.QueryUserBlocksRequest) (*types.QueryUserBlocksResponse, error) { sdkCtx := sdk.UnwrapSDKContext(ctx) - blocks := k.GetUserBlocks(sdkCtx, request.User) - return &types.QueryUserBlocksResponse{Blocks: blocks}, nil + var userblocks []types.UserBlock + + // Get user blocks prefix store + store := sdkCtx.KVStore(k.storeKey) + userBlocksStore := prefix.NewStore(store, types.BlockerSubspacePrefix(request.User, request.SubspaceId)) + + // Get paginated user blocks + pageRes, err := query.FilteredPaginate(userBlocksStore, request.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { + var userBlock types.UserBlock + if err := k.cdc.UnmarshalBinaryBare(value, &userBlock); err != nil { + return false, status.Error(codes.Internal, err.Error()) + } + + if accumulate { + userblocks = append(userblocks, userBlock) + } + return true, nil + }) + + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryUserBlocksResponse{Blocks: userblocks, Pagination: pageRes}, nil } // Params implements the Query/Params gRPC method diff --git a/x/profiles/keeper/grpc_query_test.go b/x/profiles/keeper/grpc_query_test.go index feb0fcd305..2e05d361d5 100644 --- a/x/profiles/keeper/grpc_query_test.go +++ b/x/profiles/keeper/grpc_query_test.go @@ -462,6 +462,77 @@ func (suite *KeeperTestSuite) Test_UserRelationships() { } } +func (suite *KeeperTestSuite) Test_UserBlocks() { + usecases := []struct { + name string + storedUserBlocks []types.UserBlock + req *types.QueryUserBlocksRequest + shouldErr bool + expLen int + }{ + { + name: "query blocks without pagination", + storedUserBlocks: []types.UserBlock{ + types.NewUserBlock( + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + "cosmos1s3nh6tafl4amaxkke9kdejhp09lk93g9ev39r4", + "reason1", + "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", + ), + types.NewUserBlock( + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + "reason2", + "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", + ), + }, + req: &types.QueryUserBlocksRequest{User: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"}, + shouldErr: false, + expLen: 2, + }, + { + name: "query blocks with pagination", + storedUserBlocks: []types.UserBlock{ + types.NewUserBlock( + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + "cosmos1s3nh6tafl4amaxkke9kdejhp09lk93g9ev39r4", + "reason1", + "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", + ), + types.NewUserBlock( + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + "reason2", + "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", + ), + }, + req: &types.QueryUserBlocksRequest{User: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", Pagination: &query.PageRequest{Limit: 1}}, + shouldErr: false, + expLen: 1, + }, + } + + for _, uc := range usecases { + uc := uc + suite.Run(uc.name, func() { + suite.SetupTest() + + for _, UserBlock := range uc.storedUserBlocks { + suite.k.SaveUserBlock(suite.ctx, UserBlock) + } + + res, err := suite.k.UserBlocks(sdk.WrapSDKContext(suite.ctx), uc.req) + if uc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(uc.expLen, len(res.Blocks)) + } + }) + } +} + func (suite *KeeperTestSuite) TestQueryServer_UserApplicationLinks() { usecases := []struct { name string diff --git a/x/profiles/keeper/keeper_blocks.go b/x/profiles/keeper/keeper_blocks.go index eed64105a3..3bd748dc30 100644 --- a/x/profiles/keeper/keeper_blocks.go +++ b/x/profiles/keeper/keeper_blocks.go @@ -11,47 +11,36 @@ import ( // something goes wrong. func (k Keeper) SaveUserBlock(ctx sdk.Context, userBlock types.UserBlock) error { store := ctx.KVStore(k.storeKey) - key := types.UsersBlocksStoreKey(userBlock.Blocker) - - blocks := types.MustUnmarshalUserBlocks(k.cdc, store.Get(key)) - for _, ub := range blocks { - if ub == userBlock { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, - "the user with address %s has already been blocked", userBlock.Blocked) - } + key := types.UsersBlocksStoreKey(userBlock.Blocker, userBlock.Subspace, userBlock.Blocked) + if store.Has(key) { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, + "the user with address %s has already been blocked", userBlock.Blocked) } - store.Set(key, types.MustMarshalUserBlocks(k.cdc, append(blocks, userBlock))) + store.Set(key, k.cdc.MustMarshalBinaryBare(&userBlock)) return nil } // DeleteUserBlock allows to the specified blocker to unblock the given blocked user. func (k Keeper) DeleteUserBlock(ctx sdk.Context, blocker, blocked string, subspace string) error { store := ctx.KVStore(k.storeKey) - key := types.UsersBlocksStoreKey(blocker) - - blocks := types.MustUnmarshalUserBlocks(k.cdc, store.Get(key)) - - blocks, found := types.RemoveUserBlock(blocks, blocker, blocked, subspace) - if !found { + key := types.UsersBlocksStoreKey(blocker, subspace, blocked) + if !store.Has(key) { return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "block from %s towards %s for subspace %s not found", blocker, blocked, subspace) } - - // Delete the key if no blocks are left. - // This cleans up the store avoiding export/import tests to fail due to a different number of keys present. - if len(blocks) == 0 { - store.Delete(key) - } else { - store.Set(key, types.MustMarshalUserBlocks(k.cdc, blocks)) - } + store.Delete(key) return nil } // GetUserBlocks returns the list of users that the specified user has blocked. -func (k Keeper) GetUserBlocks(ctx sdk.Context, user string) []types.UserBlock { - store := ctx.KVStore(k.storeKey) - return types.MustUnmarshalUserBlocks(k.cdc, store.Get(types.UsersBlocksStoreKey(user))) +func (k Keeper) GetUserBlocks(ctx sdk.Context, blocker string) []types.UserBlock { + var userblocks []types.UserBlock + k.IterateBlockedUsers(ctx, blocker, func(index int64, userblock types.UserBlock) (stop bool) { + userblocks = append(userblocks, userblock) + return false + }) + return userblocks } // GetAllUsersBlocks returns a list of all the users blocks inside the given context. @@ -63,8 +52,8 @@ func (k Keeper) GetAllUsersBlocks(ctx sdk.Context) []types.UserBlock { var usersBlocks []types.UserBlock for ; iterator.Valid(); iterator.Next() { - blocks := types.MustUnmarshalUserBlocks(k.cdc, iterator.Value()) - usersBlocks = append(usersBlocks, blocks...) + block := types.MustUnmarshalUserBlock(k.cdc, iterator.Value()) + usersBlocks = append(usersBlocks, block) } return usersBlocks @@ -77,11 +66,17 @@ func (k Keeper) IsUserBlocked(ctx sdk.Context, blocker, blocked string) bool { // HasUserBlocked returns true if the provided blocker has blocked the given user for the given subspace. // If the provided subspace is empty, all subspaces will be checked -func (k Keeper) HasUserBlocked(ctx sdk.Context, blocker, user, subspace string) bool { - blocks := k.GetUserBlocks(ctx, blocker) +func (k Keeper) HasUserBlocked(ctx sdk.Context, blocker, blocked, subspace string) bool { + if subspace != "" { + store := ctx.KVStore(k.storeKey) + key := types.UsersBlocksStoreKey(blocker, subspace, blocked) + return store.Has(key) + } + + blocks := k.GetUserBlocks(ctx, blocker) for _, block := range blocks { - if block.Blocked == user { + if block.Blocked == blocked { return subspace == "" || block.Subspace == subspace } } diff --git a/x/profiles/simulation/decoder_test.go b/x/profiles/simulation/decoder_test.go index 1ac5275d62..67b1680944 100644 --- a/x/profiles/simulation/decoder_test.go +++ b/x/profiles/simulation/decoder_test.go @@ -77,7 +77,7 @@ func TestDecodeStore(t *testing.T) { Value: relBz, }, { - Key: types.UsersBlocksStoreKey(firstAddr), + Key: types.UsersBlocksStoreKey(firstAddr, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", secondAddr), Value: blocksBz, }, }} diff --git a/x/profiles/types/keys.go b/x/profiles/types/keys.go index 04172d0d4d..4f0a96996d 100644 --- a/x/profiles/types/keys.go +++ b/x/profiles/types/keys.go @@ -90,9 +90,19 @@ func RelationshipsStoreKey(user, subspace, recipient string) []byte { return append(UserRelationshipsSubspacePrefix(user, subspace), []byte(recipient)...) } -// UsersBlocksStoreKey turns a user address to a key used to store a Address -> []Address couple -func UsersBlocksStoreKey(user string) []byte { - return append(UsersBlocksStorePrefix, []byte(user)...) +// BLockPrefix returns the store prefix used to store the blocks created by the given blocker +func BlockerPrefix(blocker string) []byte { + return append(UsersBlocksStorePrefix, []byte(blocker)...) +} + +// BlockerSubspacePrefix returns the store prefix used to store the blocks that the given blocker has created inside the specified subspace +func BlockerSubspacePrefix(blocker string, subspace string) []byte { + return append(BlockerPrefix(blocker), []byte(subspace)...) +} + +// UsersBlocksStoreKey returns the store key used to save the block made by the given blocker, inside the specified subspace and towards the given blocked user +func UsersBlocksStoreKey(blocker string, subspace string, blockedUser string) []byte { + return append(BlockerSubspacePrefix(blocker, subspace), []byte(blockedUser)...) } // UserChainLinksPrefix returns the store prefix used to identify all the chain links for the given user diff --git a/x/profiles/types/models_relationships.go b/x/profiles/types/models_relationships.go index 41106496ea..469e2e4478 100644 --- a/x/profiles/types/models_relationships.go +++ b/x/profiles/types/models_relationships.go @@ -118,3 +118,10 @@ func MustUnmarshalUserBlocks(cdc codec.BinaryMarshaler, bz []byte) []UserBlock { cdc.MustUnmarshalBinaryBare(bz, &wrapped) return wrapped.Blocks } + +// MustUnmarshalUserBlock deserializes the given byte array as a UserBlock using the provided BinaryMarshaler +func MustUnmarshalUserBlock(cdc codec.BinaryMarshaler, bz []byte) UserBlock { + var block UserBlock + cdc.MustUnmarshalBinaryBare(bz, &block) + return block +} diff --git a/x/profiles/types/query.pb.gw.go b/x/profiles/types/query.pb.gw.go index e12448ef84..a3797c83e8 100644 --- a/x/profiles/types/query.pb.gw.go +++ b/x/profiles/types/query.pb.gw.go @@ -247,6 +247,10 @@ func local_request_Query_UserRelationships_0(ctx context.Context, marshaler runt } +var ( + filter_Query_UserBlocks_0 = &utilities.DoubleArray{Encoding: map[string]int{"user": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + func request_Query_UserBlocks_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryUserBlocksRequest var metadata runtime.ServerMetadata @@ -269,6 +273,13 @@ func request_Query_UserBlocks_0(ctx context.Context, marshaler runtime.Marshaler return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "user", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_UserBlocks_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.UserBlocks(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err @@ -296,6 +307,13 @@ func local_request_Query_UserBlocks_0(ctx context.Context, marshaler runtime.Mar return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "user", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_UserBlocks_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.UserBlocks(ctx, &protoReq) return msg, metadata, err diff --git a/x/profiles/types/query_relationships.pb.go b/x/profiles/types/query_relationships.pb.go index 55e6839230..322a988dab 100644 --- a/x/profiles/types/query_relationships.pb.go +++ b/x/profiles/types/query_relationships.pb.go @@ -140,7 +140,9 @@ func (m *QueryUserRelationshipsResponse) GetPagination() *query.PageResponse { // endpoint type QueryUserBlocksRequest struct { // address of the user to query the blocks for - User string `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + User string `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + SubspaceId string `protobuf:"bytes,2,opt,name=subspace_id,json=subspaceId,proto3" json:"subspace_id,omitempty"` + Pagination *query.PageRequest `protobuf:"bytes,3,opt,name=pagination,proto3" json:"pagination,omitempty"` } func (m *QueryUserBlocksRequest) Reset() { *m = QueryUserBlocksRequest{} } @@ -180,7 +182,8 @@ var xxx_messageInfo_QueryUserBlocksRequest proto.InternalMessageInfo // method. type QueryUserBlocksResponse struct { // blocks represent the list of all the blocks for the queried user - Blocks []UserBlock `protobuf:"bytes,1,rep,name=blocks,proto3" json:"blocks"` + Blocks []UserBlock `protobuf:"bytes,1,rep,name=blocks,proto3" json:"blocks"` + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` } func (m *QueryUserBlocksResponse) Reset() { *m = QueryUserBlocksResponse{} } @@ -223,6 +226,13 @@ func (m *QueryUserBlocksResponse) GetBlocks() []UserBlock { return nil } +func (m *QueryUserBlocksResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + func init() { proto.RegisterType((*QueryUserRelationshipsRequest)(nil), "desmos.profiles.v1beta1.QueryUserRelationshipsRequest") proto.RegisterType((*QueryUserRelationshipsResponse)(nil), "desmos.profiles.v1beta1.QueryUserRelationshipsResponse") @@ -235,35 +245,36 @@ func init() { } var fileDescriptor_c0b8922e87a25523 = []byte{ - // 442 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x52, 0x3d, 0x8f, 0xd3, 0x40, - 0x10, 0xf5, 0xde, 0x9d, 0x4e, 0xb0, 0x11, 0x8d, 0x85, 0x38, 0x5f, 0x04, 0x4e, 0x14, 0x09, 0x88, - 0x90, 0xd8, 0x55, 0x82, 0x44, 0x41, 0x85, 0x52, 0xf0, 0xd1, 0x71, 0x96, 0x68, 0xa0, 0x88, 0x76, - 0xe3, 0x39, 0x9f, 0x85, 0xe3, 0xdd, 0xf3, 0xd8, 0x88, 0xfc, 0x03, 0x4a, 0x7e, 0xc2, 0x55, 0xfc, - 0x96, 0x2b, 0x53, 0x52, 0x21, 0x94, 0x34, 0xfc, 0x0c, 0xe4, 0xdd, 0x4d, 0xce, 0x11, 0x31, 0x74, - 0x33, 0x9e, 0x79, 0x6f, 0xde, 0x7b, 0x5e, 0x3a, 0x8a, 0x01, 0xe7, 0x0a, 0xb9, 0x2e, 0xd4, 0x79, - 0x9a, 0x01, 0xf2, 0xcf, 0x23, 0x09, 0xa5, 0x18, 0xf1, 0xcb, 0x0a, 0x8a, 0xc5, 0xb4, 0x80, 0x4c, - 0x94, 0xa9, 0xca, 0xf1, 0x22, 0xd5, 0xc8, 0x74, 0xa1, 0x4a, 0xe5, 0x9f, 0x58, 0x08, 0xdb, 0x40, - 0x98, 0x83, 0x74, 0xef, 0x26, 0x2a, 0x51, 0x66, 0x87, 0xd7, 0x95, 0x5d, 0xef, 0xde, 0x4f, 0x94, - 0x4a, 0x32, 0xe0, 0x42, 0xa7, 0x5c, 0xe4, 0xb9, 0x2a, 0x2d, 0xa1, 0x9b, 0x9e, 0xba, 0xa9, 0xe9, - 0x64, 0x75, 0xce, 0x45, 0xbe, 0x70, 0xa3, 0x71, 0x9b, 0xb4, 0xb9, 0x8a, 0x21, 0xc3, 0x7d, 0xda, - 0xba, 0xa7, 0x33, 0x55, 0x63, 0xa6, 0x56, 0x85, 0x6d, 0xdc, 0xe8, 0x89, 0xed, 0xb8, 0x14, 0x08, - 0xd6, 0xdd, 0x96, 0x50, 0x8b, 0x24, 0xcd, 0x0d, 0x97, 0xdd, 0x1d, 0x7c, 0x27, 0xf4, 0xc1, 0x59, - 0xbd, 0xf2, 0x1e, 0xa1, 0x88, 0x9a, 0x77, 0x22, 0xb8, 0xac, 0x00, 0x4b, 0xdf, 0xa7, 0x47, 0x15, - 0x42, 0x11, 0x90, 0x3e, 0x19, 0xde, 0x8e, 0x4c, 0xed, 0xf7, 0x68, 0x07, 0x2b, 0x89, 0x5a, 0xcc, - 0x60, 0x9a, 0xc6, 0xc1, 0x81, 0x19, 0xd1, 0xcd, 0xa7, 0xb7, 0xb1, 0xff, 0x8a, 0xd2, 0x9b, 0x53, - 0xc1, 0x61, 0x9f, 0x0c, 0x3b, 0xe3, 0x47, 0xcc, 0xa9, 0xac, 0x75, 0x31, 0xa3, 0x6b, 0x13, 0x28, - 0x7b, 0x27, 0x12, 0x70, 0x07, 0xa3, 0x06, 0xf2, 0xc5, 0xad, 0xaf, 0x57, 0x3d, 0xef, 0xf7, 0x55, - 0xcf, 0x1b, 0x2c, 0x09, 0x0d, 0xdb, 0x84, 0xa2, 0x56, 0x39, 0xc2, 0x5e, 0xa5, 0x67, 0xf4, 0xce, - 0x4e, 0x7a, 0xc1, 0x41, 0xff, 0x70, 0xd8, 0x19, 0x3f, 0x64, 0x2d, 0xbf, 0x96, 0x35, 0xa9, 0x27, - 0x47, 0xd7, 0x3f, 0x7b, 0x5e, 0xb4, 0xcb, 0xe0, 0xbf, 0xde, 0xe3, 0xed, 0xf1, 0x7f, 0xbd, 0x59, - 0x8d, 0x4d, 0x73, 0x83, 0xe7, 0xf4, 0xde, 0xd6, 0xd1, 0x24, 0x53, 0xb3, 0x4f, 0xff, 0xca, 0xbc, - 0x11, 0xc5, 0x47, 0x7a, 0xf2, 0x17, 0xce, 0x45, 0xf0, 0x92, 0x1e, 0x4b, 0xf3, 0x25, 0x20, 0xc6, - 0xe7, 0xa0, 0xd5, 0xe7, 0x16, 0xec, 0x4c, 0x3a, 0xdc, 0xe4, 0xcd, 0xf5, 0x2a, 0x24, 0xcb, 0x55, - 0x48, 0x7e, 0xad, 0x42, 0xf2, 0x6d, 0x1d, 0x7a, 0xcb, 0x75, 0xe8, 0xfd, 0x58, 0x87, 0xde, 0x07, - 0x96, 0xa4, 0xe5, 0x45, 0x25, 0xd9, 0x4c, 0xcd, 0xb9, 0x65, 0x7d, 0x9a, 0x09, 0x89, 0xae, 0xe6, - 0x5f, 0x6e, 0x9e, 0x6f, 0xb9, 0xd0, 0x80, 0xf2, 0xd8, 0xbc, 0xb0, 0x67, 0x7f, 0x02, 0x00, 0x00, - 0xff, 0xff, 0xbf, 0xd3, 0xa8, 0xa7, 0x79, 0x03, 0x00, 0x00, + // 451 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x93, 0xbf, 0x8e, 0xd3, 0x40, + 0x10, 0xc6, 0xbd, 0xb9, 0xd3, 0x09, 0x36, 0xa2, 0xb1, 0x10, 0xe7, 0x8b, 0xc0, 0x89, 0x22, 0x01, + 0x11, 0x12, 0xbb, 0x4a, 0xe8, 0xa8, 0x50, 0x0a, 0xfe, 0x74, 0x9c, 0x25, 0x1a, 0x9a, 0x68, 0x37, + 0x99, 0xf3, 0x59, 0x38, 0xde, 0x3d, 0x8f, 0x8d, 0xc8, 0x1b, 0x50, 0xf2, 0x06, 0x5c, 0x81, 0x78, + 0x96, 0x2b, 0x53, 0x52, 0x21, 0x94, 0x34, 0x3c, 0x06, 0xf2, 0xee, 0x26, 0xe7, 0x40, 0x2c, 0x24, + 0x2a, 0xba, 0x19, 0xcf, 0x7c, 0x33, 0xbf, 0xf9, 0xac, 0xa5, 0xc3, 0x19, 0xe0, 0x5c, 0x21, 0xd7, + 0xb9, 0x3a, 0x4b, 0x52, 0x40, 0xfe, 0x7e, 0x28, 0xa1, 0x10, 0x43, 0x7e, 0x51, 0x42, 0xbe, 0x98, + 0xe4, 0x90, 0x8a, 0x22, 0x51, 0x19, 0x9e, 0x27, 0x1a, 0x99, 0xce, 0x55, 0xa1, 0xfc, 0x63, 0x2b, + 0x61, 0x1b, 0x09, 0x73, 0x92, 0xce, 0xed, 0x58, 0xc5, 0xca, 0xf4, 0xf0, 0x2a, 0xb2, 0xed, 0x9d, + 0xbb, 0xb1, 0x52, 0x71, 0x0a, 0x5c, 0xe8, 0x84, 0x8b, 0x2c, 0x53, 0x85, 0x1d, 0xe8, 0xaa, 0x27, + 0xae, 0x6a, 0x32, 0x59, 0x9e, 0x71, 0x91, 0x2d, 0x5c, 0x69, 0xd4, 0x84, 0x36, 0x57, 0x33, 0x48, + 0x71, 0x1f, 0x5b, 0xe7, 0x64, 0xaa, 0x2a, 0xcd, 0xc4, 0x52, 0xd8, 0xc4, 0x95, 0x1e, 0xd9, 0x8c, + 0x4b, 0x81, 0x60, 0xaf, 0xdb, 0x0e, 0xd4, 0x22, 0x4e, 0x32, 0x33, 0xcb, 0xf6, 0xf6, 0xbf, 0x12, + 0x7a, 0xef, 0xb4, 0x6a, 0x79, 0x83, 0x90, 0x47, 0xf5, 0x3d, 0x11, 0x5c, 0x94, 0x80, 0x85, 0xef, + 0xd3, 0xc3, 0x12, 0x21, 0x0f, 0x48, 0x8f, 0x0c, 0x6e, 0x46, 0x26, 0xf6, 0xbb, 0xb4, 0x8d, 0xa5, + 0x44, 0x2d, 0xa6, 0x30, 0x49, 0x66, 0x41, 0xcb, 0x94, 0xe8, 0xe6, 0xd3, 0xab, 0x99, 0xff, 0x9c, + 0xd2, 0xeb, 0x55, 0xc1, 0x41, 0x8f, 0x0c, 0xda, 0xa3, 0x07, 0xcc, 0x51, 0x56, 0x5c, 0xcc, 0x70, + 0x6d, 0x0c, 0x65, 0xaf, 0x45, 0x0c, 0x6e, 0x61, 0x54, 0x53, 0x3e, 0xbd, 0xf1, 0xf1, 0xb2, 0xeb, + 0xfd, 0xbc, 0xec, 0x7a, 0xfd, 0x25, 0xa1, 0x61, 0x13, 0x28, 0x6a, 0x95, 0x21, 0xec, 0x25, 0x3d, + 0xa5, 0xb7, 0x76, 0xdc, 0x0b, 0x5a, 0xbd, 0x83, 0x41, 0x7b, 0x74, 0x9f, 0x35, 0xfc, 0x5a, 0x56, + 0x1f, 0x3d, 0x3e, 0xbc, 0xfa, 0xde, 0xf5, 0xa2, 0xdd, 0x09, 0xfe, 0x8b, 0x3d, 0xb7, 0x3d, 0xfc, + 0xeb, 0x6d, 0x96, 0xb1, 0x7e, 0x5c, 0xff, 0x33, 0xa1, 0x77, 0xb6, 0x27, 0x8d, 0x53, 0x35, 0x7d, + 0xf7, 0xbf, 0x99, 0xfe, 0x85, 0xd0, 0xe3, 0x3f, 0x08, 0x9d, 0xdb, 0xcf, 0xe8, 0x91, 0x34, 0x5f, + 0x02, 0x62, 0x2c, 0xed, 0x37, 0x5a, 0xba, 0x15, 0x3b, 0x3f, 0x9d, 0xee, 0x37, 0x23, 0x5b, 0xff, + 0x6c, 0xe4, 0xf8, 0xe5, 0xd5, 0x2a, 0x24, 0xcb, 0x55, 0x48, 0x7e, 0xac, 0x42, 0xf2, 0x69, 0x1d, + 0x7a, 0xcb, 0x75, 0xe8, 0x7d, 0x5b, 0x87, 0xde, 0x5b, 0x16, 0x27, 0xc5, 0x79, 0x29, 0xd9, 0x54, + 0xcd, 0xb9, 0xc5, 0x7b, 0x9c, 0x0a, 0x89, 0x2e, 0xe6, 0x1f, 0xae, 0x9f, 0x5c, 0xb1, 0xd0, 0x80, + 0xf2, 0xc8, 0xbc, 0x8a, 0x27, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0x40, 0x7a, 0x7f, 0xc4, 0x2d, + 0x04, 0x00, 0x00, } func (m *QueryUserRelationshipsRequest) Marshal() (dAtA []byte, err error) { @@ -391,6 +402,25 @@ func (m *QueryUserBlocksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQueryRelationships(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.SubspaceId) > 0 { + i -= len(m.SubspaceId) + copy(dAtA[i:], m.SubspaceId) + i = encodeVarintQueryRelationships(dAtA, i, uint64(len(m.SubspaceId))) + i-- + dAtA[i] = 0x12 + } if len(m.User) > 0 { i -= len(m.User) copy(dAtA[i:], m.User) @@ -421,6 +451,18 @@ func (m *QueryUserBlocksResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQueryRelationships(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } if len(m.Blocks) > 0 { for iNdEx := len(m.Blocks) - 1; iNdEx >= 0; iNdEx-- { { @@ -503,6 +545,14 @@ func (m *QueryUserBlocksRequest) Size() (n int) { if l > 0 { n += 1 + l + sovQueryRelationships(uint64(l)) } + l = len(m.SubspaceId) + if l > 0 { + n += 1 + l + sovQueryRelationships(uint64(l)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQueryRelationships(uint64(l)) + } return n } @@ -518,6 +568,10 @@ func (m *QueryUserBlocksResponse) Size() (n int) { n += 1 + l + sovQueryRelationships(uint64(l)) } } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQueryRelationships(uint64(l)) + } return n } @@ -890,6 +944,74 @@ func (m *QueryUserBlocksRequest) Unmarshal(dAtA []byte) error { } m.User = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubspaceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueryRelationships + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueryRelationships + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueryRelationships + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SubspaceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueryRelationships + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQueryRelationships + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQueryRelationships + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQueryRelationships(dAtA[iNdEx:]) @@ -974,6 +1096,42 @@ func (m *QueryUserBlocksResponse) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueryRelationships + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQueryRelationships + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQueryRelationships + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQueryRelationships(dAtA[iNdEx:]) From a82f1ed433c70b5e50000bb106594cebb38ab739 Mon Sep 17 00:00:00 2001 From: Riccardo Montagnin Date: Wed, 30 Jun 2021 14:49:22 +0200 Subject: [PATCH 2/2] Profile improvements See PR #523 --- Makefile | 6 +- docs/.vuepress/config.js | 21 +- .../public/assets/desmos-chain-link-ibc.png | Bin 0 -> 60626 bytes docs/assets/desmos-band-process.png | Bin 33635 -> 0 bytes docs/assets/desmos-band-setup.png | Bin 18407 -> 0 bytes docs/developers/types.md | 1 + .../types/profiles/application-link.md | 46 ++-- docs/developers/types/profiles/chain-link.md | 135 ++++++++++ docs/validators/common-problems.md | 2 +- proto/desmos/profiles/v1beta1/genesis.proto | 2 + .../profiles/v1beta1/models_app_links.proto | 7 + .../profiles/v1beta1/models_chain_links.proto | 5 + .../v1beta1/models_dtag_requests.proto | 1 + .../profiles/v1beta1/models_packets.proto | 4 + .../profiles/v1beta1/models_params.proto | 6 + .../v1beta1/models_relationships.proto | 5 - .../v1beta1/query_dtag_requests.proto | 3 +- .../client/cli/cli_relationships_test.go | 10 +- x/profiles/client/cli/cli_test.go | 23 +- x/profiles/keeper/alias_functions.go | 94 +++++-- x/profiles/keeper/alias_functions_test.go | 86 ++++++- x/profiles/keeper/genesis_test.go | 122 +++++---- x/profiles/keeper/grpc_query_test.go | 117 +++++---- x/profiles/keeper/invariants.go | 223 +++++++++++++++-- x/profiles/keeper/invariants_test.go | 185 ++++++++++++-- x/profiles/keeper/keeper.go | 32 ++- x/profiles/keeper/keeper_app_links.go | 21 +- x/profiles/keeper/keeper_blocks.go | 55 ++-- x/profiles/keeper/keeper_blocks_test.go | 121 ++++++--- x/profiles/keeper/keeper_chain_links.go | 59 +++-- x/profiles/keeper/keeper_chain_links_test.go | 233 +++++++++-------- x/profiles/keeper/keeper_dtag_transfers.go | 10 +- .../keeper/keeper_dtag_transfers_test.go | 119 ++++++--- x/profiles/keeper/keeper_relationships.go | 26 ++ .../keeper/keeper_relationships_test.go | 109 +++++--- x/profiles/keeper/keeper_test.go | 14 +- x/profiles/keeper/msg_server_blocks_test.go | 65 +++-- x/profiles/keeper/msg_server_chain_link.go | 2 +- .../keeper/msg_server_chain_link_test.go | 2 +- .../keeper/msg_server_dtag_transfers.go | 6 +- .../keeper/msg_server_dtag_transfers_test.go | 93 ++++--- .../keeper/msg_server_relationships_test.go | 69 +++-- x/profiles/keeper/msgs_server_profile.go | 8 +- x/profiles/keeper/relay_chain_links.go | 7 +- x/profiles/module_ibc.go | 2 +- x/profiles/simulation/decoder.go | 31 ++- x/profiles/simulation/decoder_test.go | 62 ++--- x/profiles/simulation/genesis.go | 198 +++++++++++---- .../simulation/operations_dtag_transfers.go | 64 ++--- .../simulation/operations_relationships.go | 14 +- .../simulation/operations_user_blocks.go | 7 +- x/profiles/simulation/utils.go | 22 +- x/profiles/types/account.go | 4 +- x/profiles/types/genesis.pb.go | 117 +++------ x/profiles/types/keys.go | 10 +- x/profiles/types/models_app_links.pb.go | 226 +++++------------ x/profiles/types/models_chain_links.go | 50 +++- x/profiles/types/models_chain_links.pb.go | 164 +++--------- x/profiles/types/models_chain_links_test.go | 95 +++++-- x/profiles/types/models_dtag_requests.pb.go | 68 ++--- x/profiles/types/models_packets.pb.go | 102 +++----- x/profiles/types/models_params.pb.go | 86 +++---- x/profiles/types/models_relationships.go | 14 -- x/profiles/types/models_relationships.pb.go | 236 ++---------------- x/profiles/types/models_relationships_test.go | 12 - x/profiles/types/query_dtag_requests.pb.go | 5 +- x/staging/posts/keeper/common_test.go | 87 +++++-- x/staging/posts/keeper/msgs_server_test.go | 8 +- 68 files changed, 2258 insertions(+), 1581 deletions(-) create mode 100644 docs/.vuepress/public/assets/desmos-chain-link-ibc.png delete mode 100644 docs/assets/desmos-band-process.png delete mode 100644 docs/assets/desmos-band-setup.png create mode 100644 docs/developers/types/profiles/chain-link.md diff --git a/Makefile b/Makefile index 75cb67e088..62501170ee 100644 --- a/Makefile +++ b/Makefile @@ -308,9 +308,9 @@ proto-lint: proto-check-breaking: @$(DOCKER_BUF) breaking --against $(HTTPS_GIT)#branch=master -TM_URL = https://raw.githubusercontent.com/tendermint/tendermint/v0.34.9/proto/tendermint +TM_URL = https://raw.githubusercontent.com/tendermint/tendermint/v0.34.11/proto/tendermint GOGO_PROTO_URL = https://raw.githubusercontent.com/regen-network/protobuf/cosmos -COSMOS_URL = https://raw.githubusercontent.com/cosmos/cosmos-sdk/v0.42.4/proto/cosmos +COSMOS_URL = https://raw.githubusercontent.com/cosmos/cosmos-sdk/v0.42.6/proto/cosmos COSMOS_PROTO_URL = https://raw.githubusercontent.com/regen-network/cosmos-proto/master CONFIO_URL = https://raw.githubusercontent.com/confio/ics23/v0.6.3 IBC_URL = https://raw.githubusercontent.com/cosmos/cosmos-sdk/v0.42.4/proto/ibc @@ -370,8 +370,6 @@ proto-update-deps: ## Issue link: https://github.com/confio/ics23/issues/32 @sed -i '4ioption go_package = "github.com/confio/ics23/go";' $(CONFIO_TYPES)/proofs.proto - @mkdir -p - .PHONY: proto-all proto-gen proto-lint proto-check-breaking proto-update-deps ############################################################################### diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index ed68b9c90f..df1190b58d 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -55,7 +55,26 @@ module.exports = { collapsable: true, children: [ ["developers/overview", "Overview"], - ["developers/types", "Types"], + { + title: "Types", + path: "developers/types", + collapsable: true, + children: [ + { + title: "Profiles", + collapsable: true, + children: [ + [ "developers/types/profiles/profile", "Profile" ], + [ "developers/types/profiles/profile-pictures", "Profile pictures" ], + [ "developers/types/profiles/dtag-transfer-request", "DTag transfer request" ], + [ "developers/types/profiles/relationship", "Relationship" ], + [ "developers/types/profiles/user-block", "User block" ], + [ "developers/types/profiles/application-link", "Application link" ], + [ "developers/types/profiles/chain-link", "Chain link" ], + ] + } + ] + }, ["developers/perform-transactions", "Performing transactions"], ["developers/query-data", "Querying data"], ["developers/observe-data", "Observing data"], diff --git a/docs/.vuepress/public/assets/desmos-chain-link-ibc.png b/docs/.vuepress/public/assets/desmos-chain-link-ibc.png new file mode 100644 index 0000000000000000000000000000000000000000..115a3f01e27aade0cd116c975f0ffa7a45f60ac1 GIT binary patch literal 60626 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 Vr1y3&6KL>{o{o`rfu`fd{{?Zvp!xs+ diff --git a/docs/assets/desmos-band-setup.png b/docs/assets/desmos-band-setup.png deleted file mode 100644 index fa006bdeba2b2e58f1f0f664cf668e4ec86762f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18407 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 diff --git a/docs/developers/types.md b/docs/developers/types.md index 181851eac7..2c0057f176 100644 --- a/docs/developers/types.md +++ b/docs/developers/types.md @@ -10,6 +10,7 @@ Following you can find all the custom types that are used to represent the data - [Relationship](types/profiles/relationship.md) - [User block](types/profiles/user-block.md) - [Application link](types/profiles/application-link.md) +- [Chain link](types/profiles/chain-link.md) ## Posts diff --git a/docs/developers/types/profiles/application-link.md b/docs/developers/types/profiles/application-link.md index 902a937ce6..dbee8dc155 100644 --- a/docs/developers/types/profiles/application-link.md +++ b/docs/developers/types/profiles/application-link.md @@ -1,19 +1,22 @@ # Application link -An application link (abbr. _app link_) represents a link to an external (and possibly centralized) application. These links are created by the user and their validity is checked using a multi-step verification process described inside the [_"Themis"_ repository](https://github.com/desmos-labs/themis). +An application link (abbr. _app link_) represents a link to an external (and possibly centralized) application. -## `User` +## Contained data +Here follows the data of an application link. + +### `User` Address of the Desmos profile to which the link is associated. -## `Data` +### `Data` Object that contains the details of the link. -### `Application` +#### `Application` Name of the application to which the link refers to (eg. `twitter`, `github`, `reddit`, etc). -### `Username` +#### `Username` Identifier of the application account which the link refers to (eg. Twitter username, GitHub profile, Reddit username, etc). -## `State` +### `State` Representation of the current state of the link. There can be five different states in which a link can be: - `APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED` if the link has just been created, and it still needs to be processed; @@ -22,48 +25,51 @@ Representation of the current state of the link. There can be five different sta - `APPLICATION_LINK_STATE_VERIFICATION_SUCCESS` if the verification process ended with success; - `APPLICATION_LINK_STATE_TIMED_OUT` if the verification process expired due to a timeout. -## `OracleRequest` +### `OracleRequest` The `OracleRequest` field contains all the data that has been sent to the oracle script in order to verify the authenticity of the link. -### `ID` +#### `ID` This is the unique id of the request that has been made to verify the link. -### `OracleScriptID` +#### `OracleScriptID` Unique id of the script that has been called to verify the authenticity of the link. -### `CallData` +#### `CallData` Contains the details of the data that will be used to call the oracle script. -#### `Application` +##### `Application` Name of the application for which the link is valid (eg. `twitter`, `github`, `reddit`, etc). -#### `CallData` +##### `CallData` The `CallData` field represents the hex-encoded data that will be given to the data source in order to fetch and verify the validity of the link. -### `ClientID` +#### `ClientID` ID of the client that has performed the request. -## `Result` +### `Result` The `Result` field contains the effective result of the verification process. This is set only if the link state is either `APPLICATION_LINK_STATE_VERIFICATION_SUCCESS` or `APPLICATION_LINK_STATE_VERIFICATION_ERROR`. The `Result` field can be of two types: - `Result_Success` - `Result_Error` -### `Result_Success` +#### `Result_Success` Represents a successful result. It contains two fields. -#### `Value` +##### `Value` Plain text value that has been signed from the user with their Desmos private key to prove the ownership of the Desmos profile. -#### `Signature` +##### `Signature` Hex-encoded result of the plain text value signature. -### `Result_Error` +#### `Result_Error` Identifies an error during the verification process. It contains only one field. -#### `Error` +##### `Error` Represents the description of the error that has been emitted during the verification process. -## `CreationTime` +### `CreationTime` Contains the time at which the link has been created. + +## Create an application link +Application links can be created by any user having a Desmos profile, and their validity is checked using a multi-step verification process described inside the [_"Themis"_ repository](https://github.com/desmos-labs/themis). \ No newline at end of file diff --git a/docs/developers/types/profiles/chain-link.md b/docs/developers/types/profiles/chain-link.md new file mode 100644 index 0000000000..dfd3cc6a3d --- /dev/null +++ b/docs/developers/types/profiles/chain-link.md @@ -0,0 +1,135 @@ +# Chain link +A chain link represents a link to an external chain account that has been created by the user to connect their Desmos profile to such accounts. These links can be created either offline or using IBC following the specification described inside the [_"Create a chain link"_ section](#create-a-chain-link). + +## Contained data +Here follows the data contained inside a chain link. + +### `User` +Represents the Desmos address to which the chain link is associated. + +### `Address` +Contains the data of the external chain address. Currently, it can be of two different types: + +- `Bech32Address` to represent Bech32-based addresses +- `Base58Address` to support Base58-encoded addresses + +### `Proof` +Contains the data proving the ownership of the external chain account. + +#### `PubKey` +Contains the data of the public key associated with the external address that is used to verify the ownership of such address. + +#### `Signature` +Represents the hex-encoded signature of the plain text that has been created signing it with the private key associated with the provided public key. + +#### `PlainText` +Represents the plain text value that has been signed with the private key associated with the address in order to get the signature. + +### `ChainConfig` +Contains the details of the external chain to which the link is associated. + +#### `Name` +Contains the human readable chain name. + +### `CreationTime` +Represents the time in which the link has been created. + +## Create a chain link +### 1. Create the ownership proofs +When creating a chain link, you need to provide two different proofs to make sure the link is valid: + +1. The proof that you own the external chain account +2. The proof that you own the Desmos profile to which you want to link + +In order to create a proof, the following steps are needed: + +1. Get a generic plain text data to sign +2. Sign the plain text data using your private key +3. Assemble the signature, plain text and public key into a `Proof` object. + +Here is an example of a valid proof object encoded using JSON: +```json +{ + "pub_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "A58DXR/lXKVkIjLofXgST/OHi+pkOQbVIiOjnTy7Zoqo" + }, + "signature": "ecc6175e730917fb289d3a9f4e49a5630a44b42d972f481342f540e09def2ec5169780d85c4e060d52cc3ffb3d677745a4d56cd385760735bc6db0f1816713be", + "plain_text": "cosmos15uc89vnzufu5kuhhsxdkltt38zfx8vcyggzwfm" +} +``` + +Note that the `pub_key` field must be encoded using Protobuf and must be compatible with the public key types that are currently supported by Cosmos. You can see a list of such key types [here](https://github.com/cosmos/cosmos-sdk/tree/master/proto/cosmos/crypto). + +### 2. Create the link +Once you have created the two ownership proofs, you are now ready to create the link. This can be done in two ways: +1. [Using IBC](#using-ibc) +2. [Usign a chain message](#using-chain-message) + +#### Using IBC +This is the way that you want to use when integrating the Desmos connection from your chain. +To implement the IBC capability of connecting an external account to a Desmos profile, the `x/profiles` module supports the following packet data type: + +```go +// LinkChainAccountPacketData defines the object that should be sent inside a +// MsgSendPacket when wanting to link an external chain to a Desmos profile +// using IBC +type LinkChainAccountPacketData struct { + // SourceAddress contains the details of the external chain address + SourceAddress *types.Any `protobuf:"bytes,1,opt,name=source_address,json=sourceAddress,proto3" json:"source_address,omitempty" yaml:"source_address"` + // SourceProof represents the proof of ownership of the source address + SourceProof Proof `protobuf:"bytes,2,opt,name=source_proof,json=sourceProof,proto3" json:"source_proof" yaml:"source_proof"` + // SourceChainConfig contains the details of the source chain + SourceChainConfig ChainConfig `protobuf:"bytes,3,opt,name=source_chain_config,json=sourceChainConfig,proto3" json:"source_chain_config" yaml:"source_chain_config"` + // DestinationAddress represents the Desmos address of the profile that should + // be linked with the external account + DestinationAddress string `protobuf:"bytes,4,opt,name=destination_address,json=destinationAddress,proto3" json:"destination_address,omitempty" yaml:"destination_address"` + // DestinationProof contains the proof of ownership of the DestinationAddress + DestinationProof Proof `protobuf:"bytes,5,opt,name=destination_proof,json=destinationProof,proto3" json:"destination_proof" yaml:"destination_proof"` +} +``` + +| Field | Type | Description | +| :----: | :-----: | :------ | +| `SourceAddress` | `AddressData` | Contains the details of the external address to be linked | +| `SourceProof` | `Proof` | Contains the data to verify the ownership of the external address | +| `SourceChainConfig` | `ChainConfig` | Contains the data of the external chain to be linked | +| `DestinationAddress` | `string` | Desmos address of the profile to which link the external address | +| `DestinationProof` | `Proof` | Proof of ownership of the Desmos profile | + +Note that the `SourceAddress` field must one of the currently supported types: +- `Base58Address` if the external address is represented by the Base58 encoded public key of the account +- `Bech32Address` if the external address is Bech 32 encoded + +The overall view is the following: + + + +#### Using a chain message +In the case you don't want or cannot integrate IBC into your project, you can always rely on `MsgLinkChainAccount` to link an external profile to a Desmos profile: + +```go +// MsgLinkChainAccount represents a message to link an account to a profile. +type MsgLinkChainAccount struct { + // ChainAddress contains the details of the external chain address to be + // linked + ChainAddress *types.Any `protobuf:"bytes,1,opt,name=chain_address,json=chainAddress,proto3" json:"chain_address,omitempty" yaml:"source_address"` + // Proof contains the proof of ownership of the external chain address + Proof Proof `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof" yaml:"source_proof"` + // ChainConfig contains the configuration of the external chain + ChainConfig ChainConfig `protobuf:"bytes,3,opt,name=chain_config,json=chainConfig,proto3" json:"chain_config" yaml:"source_chain_config"` + // Signer represents the Desmos address associated with the + // profile to which link the external account + Signer string `protobuf:"bytes,4,opt,name=signer,proto3" json:"signer,omitempty" yaml:"signer"` +} +``` + +| Field | Type | Description | +| :----: | :-----: | :------ | +| `ChainAddress` | `AddressData` | Contains the details of the external address to be linked | +| `Proof` | `Proof` | Contains the data to verify the ownership of the external address | +| `ChainConfig` | `ChainConfig` | Contains the data of the external chain to be linked | +| `Signer` | `string` | Desmos address of the profile to which link the external address, which must be the signer of the transaction | + +##### Using the CLI +Note that you can compose this message using the `desmos tx profiles link-chain` command. \ No newline at end of file diff --git a/docs/validators/common-problems.md b/docs/validators/common-problems.md index 0565588270..6061dc0bc8 100644 --- a/docs/validators/common-problems.md +++ b/docs/validators/common-problems.md @@ -173,7 +173,7 @@ Instead of using a seed node, you can also keep relying on persistent peers. In /net_info ``` -For example, you can use the public RPC endpoint [here](http://rpc.morpheus.desmos.network:26657/net_info). +For example, you can use the public RPC endpoint [here](https://rpc.morpheus.desmos.network/net_info). From that page, you can see all the peers connected to that node. Their info is present inside the `peers` field, which contains a list of objects made as follows: diff --git a/proto/desmos/profiles/v1beta1/genesis.proto b/proto/desmos/profiles/v1beta1/genesis.proto index b74737cd42..a6e4569b45 100644 --- a/proto/desmos/profiles/v1beta1/genesis.proto +++ b/proto/desmos/profiles/v1beta1/genesis.proto @@ -13,6 +13,8 @@ option go_package = "github.com/desmos-labs/desmos/x/profiles/types"; // GenesisState defines the profiles module's genesis state. message GenesisState { + option (gogoproto.goproto_getters) = false; + repeated desmos.profiles.v1beta1.DTagTransferRequest dtag_transfer_requests = 1 [ (gogoproto.nullable) = false, diff --git a/proto/desmos/profiles/v1beta1/models_app_links.proto b/proto/desmos/profiles/v1beta1/models_app_links.proto index 5272eddb62..950e67a2f5 100644 --- a/proto/desmos/profiles/v1beta1/models_app_links.proto +++ b/proto/desmos/profiles/v1beta1/models_app_links.proto @@ -8,6 +8,7 @@ import "google/protobuf/timestamp.proto"; // ApplicationLink contains the data of a link to a centralized application message ApplicationLink { + option (gogoproto.goproto_getters) = false; option (gogoproto.equal) = true; // User to which the link is associated @@ -41,6 +42,7 @@ message ApplicationLink { // Data contains the data associated to a specific user of a // generic centralized application message Data { + option (gogoproto.goproto_getters) = false; option (gogoproto.equal) = true; // The application name (eg. Twitter, GitHub, etc) @@ -52,6 +54,8 @@ message Data { // OracleRequest represents a generic oracle request used to // verify the ownership of a centralized application account message OracleRequest { + option (gogoproto.goproto_getters) = false; + option (gogoproto.equal) = true; // ID is the ID of the request @@ -114,6 +118,7 @@ enum ApplicationLinkState { // Result represents a verification result message Result { + option (gogoproto.goproto_getters) = false; option (gogoproto.equal) = true; // sum is the oneof that specifies whether this represents a success or @@ -129,6 +134,7 @@ message Result { // Success is the result of an application link that has been successfully // verified message Success { + option (gogoproto.goproto_getters) = false; option (gogoproto.equal) = true; // Value that has be signed by the profile @@ -140,6 +146,7 @@ message Result { // Failed is the result of an application link that has not been verified // successfully message Failed { + option (gogoproto.goproto_getters) = false; option (gogoproto.equal) = true; // Error that is associated with the failure diff --git a/proto/desmos/profiles/v1beta1/models_chain_links.proto b/proto/desmos/profiles/v1beta1/models_chain_links.proto index b05c5ad2f7..3547826cbc 100644 --- a/proto/desmos/profiles/v1beta1/models_chain_links.proto +++ b/proto/desmos/profiles/v1beta1/models_chain_links.proto @@ -11,6 +11,7 @@ option go_package = "github.com/desmos-labs/desmos/x/profiles/types"; // ChainLink contains the data representing either an inter- or cross- chain // link message ChainLink { + option (gogoproto.goproto_getters) = false; option (gogoproto.equal) = true; option (gogoproto.goproto_stringer) = true; @@ -44,6 +45,7 @@ message ChainLink { // ChainConfig contains the data of the chain with which the link is made. message ChainConfig { + option (gogoproto.goproto_getters) = false; option (gogoproto.equal) = true; option (gogoproto.goproto_stringer) = true; @@ -53,6 +55,7 @@ message ChainConfig { // Proof contains all the data used to verify a signature when linking an // account to a profile message Proof { + option (gogoproto.goproto_getters) = false; option (gogoproto.equal) = true; option (gogoproto.goproto_stringer) = true; @@ -69,6 +72,7 @@ message Proof { // Bech32Address represents a Bech32-encoded address message Bech32Address { + option (gogoproto.goproto_getters) = false; option (gogoproto.equal) = true; option (gogoproto.goproto_stringer) = true; option (cosmos_proto.implements_interface) = "AddressData"; @@ -82,6 +86,7 @@ message Bech32Address { // Base58Address represents a Base58-encoded address message Base58Address { + option (gogoproto.goproto_getters) = false; option (gogoproto.equal) = true; option (gogoproto.goproto_stringer) = true; option (cosmos_proto.implements_interface) = "AddressData"; diff --git a/proto/desmos/profiles/v1beta1/models_dtag_requests.proto b/proto/desmos/profiles/v1beta1/models_dtag_requests.proto index 7a0e68c8a1..18a9da1793 100644 --- a/proto/desmos/profiles/v1beta1/models_dtag_requests.proto +++ b/proto/desmos/profiles/v1beta1/models_dtag_requests.proto @@ -11,6 +11,7 @@ option go_package = "github.com/desmos-labs/desmos/x/profiles/types"; // DTagTransferRequest represent a DTag transfer request between two users message DTagTransferRequest { + option (gogoproto.goproto_getters) = false; option (gogoproto.equal) = true; option (gogoproto.goproto_stringer) = true; diff --git a/proto/desmos/profiles/v1beta1/models_packets.proto b/proto/desmos/profiles/v1beta1/models_packets.proto index a98f8698fc..f8c30eeb94 100644 --- a/proto/desmos/profiles/v1beta1/models_packets.proto +++ b/proto/desmos/profiles/v1beta1/models_packets.proto @@ -12,6 +12,8 @@ option go_package = "github.com/desmos-labs/desmos/x/profiles/types"; // MsgSendPacket when wanting to link an external chain to a Desmos profile // using IBC message LinkChainAccountPacketData { + option (gogoproto.goproto_getters) = false; + // SourceAddress contains the details of the external chain address google.protobuf.Any source_address = 1 [ (gogoproto.moretags) = "yaml:\"source_address\"", @@ -44,6 +46,8 @@ message LinkChainAccountPacketData { // LinkChainAccountPacketAck defines a struct for the packet acknowledgment message LinkChainAccountPacketAck { + option (gogoproto.goproto_getters) = false; + // SourceAddress contains the external address that has been linked properly // with the profile string source_address = 1; diff --git a/proto/desmos/profiles/v1beta1/models_params.proto b/proto/desmos/profiles/v1beta1/models_params.proto index 72f1b0866b..e996da00ff 100644 --- a/proto/desmos/profiles/v1beta1/models_params.proto +++ b/proto/desmos/profiles/v1beta1/models_params.proto @@ -7,6 +7,8 @@ option go_package = "github.com/desmos-labs/desmos/x/profiles/types"; // Params contains the parameters for the profiles module message Params { + option (gogoproto.goproto_getters) = false; + NicknameParams nickname_params = 1 [ (gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"nickname_params\"" @@ -27,6 +29,8 @@ message Params { // NicknameParams defines the parameters related to the profiles nicknames message NicknameParams { + option (gogoproto.goproto_getters) = false; + bytes min_nickname_length = 1 [ (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false, @@ -42,6 +46,8 @@ message NicknameParams { // DTagParams defines the parameters related to profile DTags message DTagParams { + option (gogoproto.goproto_getters) = false; + string reg_ex = 1 [ (gogoproto.moretags) = "yaml:\"reg_ex\"" ]; bytes min_dtag_length = 2 [ diff --git a/proto/desmos/profiles/v1beta1/models_relationships.proto b/proto/desmos/profiles/v1beta1/models_relationships.proto index dee495d7c1..42f694aea1 100644 --- a/proto/desmos/profiles/v1beta1/models_relationships.proto +++ b/proto/desmos/profiles/v1beta1/models_relationships.proto @@ -39,8 +39,3 @@ message UserBlock { // blocked string subspace = 4 [ (gogoproto.moretags) = "yaml:\"subspace\"" ]; } - -// UserBlocks wraps a list of UserBlock objects -message UserBlocks { - repeated UserBlock blocks = 1 [ (gogoproto.nullable) = false ]; -} \ No newline at end of file diff --git a/proto/desmos/profiles/v1beta1/query_dtag_requests.proto b/proto/desmos/profiles/v1beta1/query_dtag_requests.proto index 9ecac196a7..e25661e512 100644 --- a/proto/desmos/profiles/v1beta1/query_dtag_requests.proto +++ b/proto/desmos/profiles/v1beta1/query_dtag_requests.proto @@ -16,7 +16,8 @@ message QueryIncomingDTagTransferRequestsRequest { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; - // Receiver represents the address of the user to which query the incoming requests for + // Receiver represents the address of the user to which query the incoming + // requests for string receiver = 1; // Pagination defines an optional pagination for the request diff --git a/x/profiles/client/cli/cli_relationships_test.go b/x/profiles/client/cli/cli_relationships_test.go index ac39b9c02c..fce28ef4cd 100644 --- a/x/profiles/client/cli/cli_relationships_test.go +++ b/x/profiles/client/cli/cli_relationships_test.go @@ -42,16 +42,16 @@ func (s *IntegrationTestSuite) TestCmdQueryUserRelationships() { { name: "existing relationship is returned properly", args: []string{ - "cosmos122u6u9gpdr2rp552fkkvlgyecjlmtqhkascl5a", + "cosmos1ftkjv8njvkekk00ehwdfl5sst8zgdpenjfm4hs", fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, expectErr: false, expectedOutput: types.QueryUserRelationshipsResponse{ - User: "cosmos122u6u9gpdr2rp552fkkvlgyecjlmtqhkascl5a", + User: "cosmos1ftkjv8njvkekk00ehwdfl5sst8zgdpenjfm4hs", Relationships: []types.Relationship{ types.NewRelationship( - "cosmos122u6u9gpdr2rp552fkkvlgyecjlmtqhkascl5a", "cosmos1ftkjv8njvkekk00ehwdfl5sst8zgdpenjfm4hs", + "cosmos1zs70glquczqgt83g03jnvcqppu4jjj8yjxwlvh", "60303ae22b998861bce3b28f33eec1be758a213c86c93c076dbe9f558c11c752", ), }, @@ -111,14 +111,14 @@ func (s *IntegrationTestSuite) TestCmdQueryUserBlocks() { { name: "existing user blocks are returned properly", args: []string{ - "cosmos1azqm9kmyxunkx2yt332hmnr8sa3lclhjlg9w5k", + "cosmos1ftkjv8njvkekk00ehwdfl5sst8zgdpenjfm4hs", fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, expectErr: false, expectedOutput: types.QueryUserBlocksResponse{ Blocks: []types.UserBlock{ types.NewUserBlock( - "cosmos1azqm9kmyxunkx2yt332hmnr8sa3lclhjlg9w5k", + "cosmos1ftkjv8njvkekk00ehwdfl5sst8zgdpenjfm4hs", "cosmos1zs70glquczqgt83g03jnvcqppu4jjj8yjxwlvh", "Test block", "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", diff --git a/x/profiles/client/cli/cli_test.go b/x/profiles/client/cli/cli_test.go index 6def51da2c..dff755e06b 100644 --- a/x/profiles/client/cli/cli_test.go +++ b/x/profiles/client/cli/cli_test.go @@ -118,7 +118,7 @@ func (s *IntegrationTestSuite) SetupSuite() { } profilesData.Blocks = []types.UserBlock{ types.NewUserBlock( - "cosmos1azqm9kmyxunkx2yt332hmnr8sa3lclhjlg9w5k", + addr.String(), "cosmos1zs70glquczqgt83g03jnvcqppu4jjj8yjxwlvh", "Test block", "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", @@ -126,8 +126,8 @@ func (s *IntegrationTestSuite) SetupSuite() { } profilesData.Relationships = []types.Relationship{ types.NewRelationship( - "cosmos122u6u9gpdr2rp552fkkvlgyecjlmtqhkascl5a", - "cosmos1ftkjv8njvkekk00ehwdfl5sst8zgdpenjfm4hs", + addr.String(), + "cosmos1zs70glquczqgt83g03jnvcqppu4jjj8yjxwlvh", "60303ae22b998861bce3b28f33eec1be758a213c86c93c076dbe9f558c11c752", ), } @@ -154,21 +154,14 @@ func (s *IntegrationTestSuite) SetupSuite() { "cosmospub1addwnpepqvryxhhqhw52c4ny5twtfzf3fsrjqhx0x5cuya0fylw0wu0eqptykeqhr4d", ) s.Require().NoError(err) + + stringAddr, err := sdk.Bech32ifyAddressBytes("cosmos", pubKey.Address()) + s.Require().NoError(err) + profilesData.ChainLinks = []types.ChainLink{ types.NewChainLink( "cosmos1ftkjv8njvkekk00ehwdfl5sst8zgdpenjfm4hs", - types.NewBech32Address("cosmos1xmquc944hzu6n6qtljcexkuhhz76mucxtgm5x0", "cosmos"), - types.NewProof( - pubKey, - "909e38994b1583d3f14384c2e9a03c90064e8fd8e19b780bb0ba303dfe671a27287da04d0ce096ce9a140bd070ee36818f5519eb2070a16971efd8143855524b", - "text", - ), - types.NewChainConfig("cosmos"), - time.Date(2019, 1, 1, 00, 00, 00, 000, time.UTC), - ), - types.NewChainLink( - "cosmos1ftkjv8njvkekk00ehwdfl5sst8zgdpenjfm4hs", - types.NewBech32Address("cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", "cosmos"), + types.NewBech32Address(stringAddr, "cosmos"), types.NewProof( pubKey, "909e38994b1583d3f14384c2e9a03c90064e8fd8e19b780bb0ba303dfe671a27287da04d0ce096ce9a140bd070ee36818f5519eb2070a16971efd8143855524b", diff --git a/x/profiles/keeper/alias_functions.go b/x/profiles/keeper/alias_functions.go index 544defbc8a..cd0113632d 100644 --- a/x/profiles/keeper/alias_functions.go +++ b/x/profiles/keeper/alias_functions.go @@ -33,8 +33,36 @@ func (k Keeper) GetProfiles(ctx sdk.Context) []*types.Profile { return profiles } +// HasProfile returns true iff the given user has a profile, or an error if something is wrong. +func (k Keeper) HasProfile(ctx sdk.Context, user string) bool { + _, found, err := k.GetProfile(ctx, user) + if err != nil { + return false + } + return found +} + // -------------------------------------------------------------------------------------------------------------------- +// IterateDTagTransferRequests iterates over all the DTag transfer requests and performs the provided function +func (k Keeper) IterateDTagTransferRequests( + ctx sdk.Context, fn func(index int64, dTagTransferRequest types.DTagTransferRequest) (stop bool), +) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.DTagTransferRequestPrefix) + defer iterator.Close() + + i := int64(0) + for ; iterator.Valid(); iterator.Next() { + request := types.MustUnmarshalDTagTransferRequest(k.cdc, iterator.Value()) + stop := fn(i, request) + if stop { + break + } + i++ + } +} + // IterateUserIncomingDTagTransferRequests iterates over all the DTag transfer request made to the given user // and performs the provided function func (k Keeper) IterateUserIncomingDTagTransferRequests( @@ -101,8 +129,25 @@ func (k Keeper) IterateUserRelationships(ctx sdk.Context, user string, fn func(i } } -// IterateBlockedUsers iterates through the list of users blocked by the specified user and performs the given function -func (k Keeper) IterateBlockedUsers(ctx sdk.Context, user string, fn func(index int64, blocks types.UserBlock) (stop bool)) { +// IterateBlocks iterates through the list of user blocks and performs the given function +func (k Keeper) IterateBlocks(ctx sdk.Context, fn func(index int64, block types.UserBlock) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.UsersBlocksStorePrefix) + defer iterator.Close() + + i := int64(0) + for ; iterator.Valid(); iterator.Next() { + block := types.MustUnmarshalUserBlock(k.cdc, iterator.Value()) + stop := fn(i, block) + if stop { + break + } + i++ + } +} + +// IterateUserBlocks iterates through the list of user blocks created by the specified user and performs the given function +func (k Keeper) IterateUserBlocks(ctx sdk.Context, user string, fn func(index int64, block types.UserBlock) (stop bool)) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, types.BlockerPrefix(user)) defer iterator.Close() @@ -120,21 +165,34 @@ func (k Keeper) IterateBlockedUsers(ctx sdk.Context, user string, fn func(index // -------------------------------------------------------------------------------------------------------------------- -// IterateUserApplicationLinks iterates through all the application links realted to the given user +// IterateApplicationLinks iterates through all the application links and performs the provided function +func (k Keeper) IterateApplicationLinks(ctx sdk.Context, fn func(index int64, link types.ApplicationLink) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.UserApplicationLinkPrefix) + defer iterator.Close() + + i := int64(0) + for ; iterator.Valid(); iterator.Next() { + link := types.MustUnmarshalApplicationLink(k.cdc, iterator.Value()) + stop := fn(i, link) + if stop { + break + } + i++ + } +} + +// IterateUserApplicationLinks iterates through all the application links related to the given user // and performs the provided function func (k Keeper) IterateUserApplicationLinks(ctx sdk.Context, user string, fn func(index int64, link types.ApplicationLink) (stop bool)) { store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, types.UserApplicationLinksPrefix(user)) defer iterator.Close() i := int64(0) - for ; iterator.Valid(); iterator.Next() { link := types.MustUnmarshalApplicationLink(k.cdc, iterator.Value()) - stop := fn(i, link) - if stop { break } @@ -146,16 +204,10 @@ func (k Keeper) IterateUserApplicationLinks(ctx sdk.Context, user string, fn fun // applications links entries stored inside the current context func (k Keeper) GetApplicationLinks(ctx sdk.Context) []types.ApplicationLink { var links []types.ApplicationLink - - k.ak.IterateAccounts(ctx, func(account authtypes.AccountI) (stop bool) { - k.IterateUserApplicationLinks(ctx, account.GetAddress().String(), func(_ int64, link types.ApplicationLink) (stop bool) { - links = append(links, link) - return false - }) - + k.IterateApplicationLinks(ctx, func(index int64, link types.ApplicationLink) (stop bool) { + links = append(links, link) return false }) - return links } @@ -164,14 +216,12 @@ func (k Keeper) GetApplicationLinks(ctx sdk.Context) []types.ApplicationLink { // IterateChainLinks iterates through the chain links and perform the provided function func (k Keeper) IterateChainLinks(ctx sdk.Context, fn func(index int64, link types.ChainLink) (stop bool)) { store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, types.ChainLinksPrefix) defer iterator.Close() i := int64(0) for ; iterator.Valid(); iterator.Next() { link := types.MustUnmarshalChainLink(k.cdc, iterator.Value()) - stop := fn(i, link) if stop { break @@ -198,3 +248,13 @@ func (k Keeper) IterateUserChainLinks(ctx sdk.Context, user string, fn func(inde i++ } } + +// GetChainLinks allows to returns the list of all stored chain links +func (k Keeper) GetChainLinks(ctx sdk.Context) []types.ChainLink { + var links []types.ChainLink + k.IterateChainLinks(ctx, func(_ int64, link types.ChainLink) (stop bool) { + links = append(links, link) + return false + }) + return links +} diff --git a/x/profiles/keeper/alias_functions_test.go b/x/profiles/keeper/alias_functions_test.go index a2b23b7810..60562cc8fa 100644 --- a/x/profiles/keeper/alias_functions_test.go +++ b/x/profiles/keeper/alias_functions_test.go @@ -3,6 +3,8 @@ package keeper_test import ( "time" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -118,7 +120,11 @@ func (suite *KeeperTestSuite) TestKeeper_IterateUserIncomingDTagTransferRequests } for _, request := range requests { - err := suite.k.SaveDTagTransferRequest(suite.ctx, request) + profile := suite.CreateProfileFromAddress(address) + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.SaveDTagTransferRequest(suite.ctx, request) suite.Require().NoError(err) } @@ -247,3 +253,81 @@ func (suite *KeeperTestSuite) TestKeeper_GetApplicationLinksEntries() { suite.Require().Equal(links, suite.k.GetApplicationLinks(ctx)) } + +func (suite *KeeperTestSuite) TestKeeper_GetChainLinks() { + pub1 := secp256k1.GenPrivKey().PubKey() + pub2 := secp256k1.GenPrivKey().PubKey() + + tests := []struct { + name string + store func() + expStored []types.ChainLink + }{ + { + name: "Non existent link returns empty array", + expStored: []types.ChainLink{}, + }, + { + name: "Existent links returns all links", + store: func() { + store := suite.ctx.KVStore(suite.storeKey) + store.Set( + types.ChainLinksStoreKey("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", "cosmos", "cosmos10clxpupsmddtj7wu7g0wdysajqwp890mva046f"), + types.MustMarshalChainLink( + suite.cdc, + types.NewChainLink( + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + types.NewBech32Address("cosmos10clxpupsmddtj7wu7g0wdysajqwp890mva046f", "cosmos"), + types.NewProof(pub1, "signature", "plain_text"), + types.NewChainConfig("cosmos"), + time.Date(2020, 1, 2, 00, 00, 00, 000, time.UTC), + ), + ), + ) + store.Set( + types.ChainLinksStoreKey("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", "cosmos", "cosmos1ftkjv8njvkekk00ehwdfl5sst8zgdpenjfm4hs"), + types.MustMarshalChainLink( + suite.cdc, + types.NewChainLink( + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + types.NewBech32Address("cosmos1ftkjv8njvkekk00ehwdfl5sst8zgdpenjfm4hs", "cosmos"), + types.NewProof(pub2, "signature", "plain_text"), + types.NewChainConfig("cosmos"), + time.Date(2020, 1, 2, 00, 00, 00, 000, time.UTC), + ), + ), + ) + }, + expStored: []types.ChainLink{ + types.NewChainLink( + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + types.NewBech32Address("cosmos10clxpupsmddtj7wu7g0wdysajqwp890mva046f", "cosmos"), + types.NewProof(pub1, "signature", "plain_text"), + types.NewChainConfig("cosmos"), + time.Date(2020, 1, 2, 00, 00, 00, 000, time.UTC), + ), + types.NewChainLink( + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + types.NewBech32Address("cosmos1ftkjv8njvkekk00ehwdfl5sst8zgdpenjfm4hs", "cosmos"), + types.NewProof(pub2, "signature", "plain_text"), + types.NewChainConfig("cosmos"), + time.Date(2020, 1, 2, 00, 00, 00, 000, time.UTC), + ), + }, + }, + } + + for _, test := range tests { + suite.Run(test.name, func() { + suite.SetupTest() + if test.store != nil { + test.store() + } + links := suite.k.GetChainLinks(suite.ctx) + suite.Require().Equal(len(test.expStored), len(links)) + for _, link := range links { + suite.Require().Contains(test.expStored, link) + } + }) + } +} diff --git a/x/profiles/keeper/genesis_test.go b/x/profiles/keeper/genesis_test.go index 7257752cf9..7941918416 100644 --- a/x/profiles/keeper/genesis_test.go +++ b/x/profiles/keeper/genesis_test.go @@ -32,9 +32,19 @@ func (suite *KeeperTestSuite) Test_ExportGenesis() { { name: "non-empty state", store: func(ctx sdk.Context) { + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + dTagRequests := []types.DTagTransferRequest{ - types.NewDTagTransferRequest("dtag-1", "sender-1", "receiver-1"), - types.NewDTagTransferRequest("dtag-2", "sender-2", "receiver-2"), + types.NewDTagTransferRequest("dtag-2", "sender-2", suite.testData.otherUser), + types.NewDTagTransferRequest("dtag-1", "sender-1", suite.testData.user), } for _, req := range dTagRequests { suite.Require().NoError(suite.k.SaveDTagTransferRequest(ctx, req)) @@ -42,13 +52,13 @@ func (suite *KeeperTestSuite) Test_ExportGenesis() { relationships := []types.Relationship{ types.NewRelationship( - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + suite.testData.otherUser, + suite.testData.user, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), types.NewRelationship( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.user, + suite.testData.otherUser, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), } @@ -58,14 +68,14 @@ func (suite *KeeperTestSuite) Test_ExportGenesis() { blocks := []types.UserBlock{ types.NewUserBlock( - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + suite.testData.otherUser, + suite.testData.user, "reason", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), types.NewUserBlock( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.user, + suite.testData.otherUser, "reason", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), @@ -85,18 +95,7 @@ func (suite *KeeperTestSuite) Test_ExportGenesis() { chainLinks := []types.ChainLink{ types.NewChainLink( "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - types.NewBech32Address("cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", "cosmos"), - types.NewProof( - pubKey, - "909e38994b1583d3f14384c2e9a03c90064e8fd8e19b780bb0ba303dfe671a27287da04d0ce096ce9a140bd070ee36818f5519eb2070a16971efd8143855524b", - "text", - ), - types.NewChainConfig("cosmos"), - time.Date(2019, 1, 1, 00, 00, 00, 000, time.UTC), - ), - types.NewChainLink( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - types.NewBech32Address("cosmos1xmquc944hzu6n6qtljcexkuhhz76mucxtgm5x0", "cosmos"), + types.NewBech32Address("cosmos1nc54z3kzyal57w6wcf5khmwrxx5rafnwvu0m5z", "cosmos"), types.NewProof( pubKey, "909e38994b1583d3f14384c2e9a03c90064e8fd8e19b780bb0ba303dfe671a27287da04d0ce096ce9a140bd070ee36818f5519eb2070a16971efd8143855524b", @@ -133,31 +132,31 @@ func (suite *KeeperTestSuite) Test_ExportGenesis() { }, expGenesis: types.NewGenesisState( []types.DTagTransferRequest{ - types.NewDTagTransferRequest("dtag-1", "sender-1", "receiver-1"), - types.NewDTagTransferRequest("dtag-2", "sender-2", "receiver-2"), + types.NewDTagTransferRequest("dtag-2", "sender-2", suite.testData.otherUser), + types.NewDTagTransferRequest("dtag-1", "sender-1", suite.testData.user), }, []types.Relationship{ types.NewRelationship( - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + suite.testData.otherUser, + suite.testData.user, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), types.NewRelationship( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.user, + suite.testData.otherUser, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), }, []types.UserBlock{ types.NewUserBlock( - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + suite.testData.otherUser, + suite.testData.user, "reason", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), types.NewUserBlock( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.user, + suite.testData.otherUser, "reason", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), @@ -171,18 +170,7 @@ func (suite *KeeperTestSuite) Test_ExportGenesis() { []types.ChainLink{ types.NewChainLink( "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - types.NewBech32Address("cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", "cosmos"), - types.NewProof( - pubKey, - "909e38994b1583d3f14384c2e9a03c90064e8fd8e19b780bb0ba303dfe671a27287da04d0ce096ce9a140bd070ee36818f5519eb2070a16971efd8143855524b", - "text", - ), - types.NewChainConfig("cosmos"), - time.Date(2019, 1, 1, 00, 00, 00, 000, time.UTC), - ), - types.NewChainLink( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - types.NewBech32Address("cosmos1xmquc944hzu6n6qtljcexkuhhz76mucxtgm5x0", "cosmos"), + types.NewBech32Address("cosmos1nc54z3kzyal57w6wcf5khmwrxx5rafnwvu0m5z", "cosmos"), types.NewProof( pubKey, "909e38994b1583d3f14384c2e9a03c90064e8fd8e19b780bb0ba303dfe671a27287da04d0ce096ce9a140bd070ee36818f5519eb2070a16971efd8143855524b", @@ -319,37 +307,43 @@ func (suite *KeeperTestSuite) Test_InitGenesis() { profile2 := suite.CreateProfileFromAddress("cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns") suite.ak.SetAccount(ctx, profile2) + err := suite.k.StoreProfile(suite.ctx, profile1) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, profile2) + suite.Require().NoError(err) + addr3, err := sdk.AccAddressFromBech32("cosmos1s3nh6tafl4amaxkke9kdejhp09lk93g9ev39r4") suite.Require().NoError(err) suite.ak.SetAccount(ctx, authtypes.NewBaseAccountWithAddress(addr3)) }, genesis: types.NewGenesisState( []types.DTagTransferRequest{ - types.NewDTagTransferRequest("dtag-1", "sender-1", "receiver-1"), - types.NewDTagTransferRequest("dtag-2", "sender-2", "receiver-2"), + types.NewDTagTransferRequest("dtag-1", "sender-1", suite.testData.user), + types.NewDTagTransferRequest("dtag-2", "sender-2", suite.testData.otherUser), }, []types.Relationship{ types.NewRelationship( - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + suite.testData.user, + suite.testData.otherUser, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), types.NewRelationship( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.otherUser, + suite.testData.user, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), }, []types.UserBlock{ types.NewUserBlock( - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + suite.testData.user, + suite.testData.otherUser, "reason", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), types.NewUserBlock( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.otherUser, + suite.testData.user, "reason", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), @@ -391,20 +385,20 @@ func (suite *KeeperTestSuite) Test_InitGenesis() { ), check: func(ctx sdk.Context) { requests := []types.DTagTransferRequest{ - types.NewDTagTransferRequest("dtag-1", "sender-1", "receiver-1"), - types.NewDTagTransferRequest("dtag-2", "sender-2", "receiver-2"), + types.NewDTagTransferRequest("dtag-2", "sender-2", suite.testData.otherUser), + types.NewDTagTransferRequest("dtag-1", "sender-1", suite.testData.user), } suite.Require().Equal(requests, suite.k.GetDTagTransferRequests(ctx)) relationships := []types.Relationship{ types.NewRelationship( - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + suite.testData.otherUser, + suite.testData.user, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), types.NewRelationship( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.user, + suite.testData.otherUser, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), } @@ -412,14 +406,14 @@ func (suite *KeeperTestSuite) Test_InitGenesis() { blocks := []types.UserBlock{ types.NewUserBlock( - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + suite.testData.otherUser, + suite.testData.user, "reason", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), types.NewUserBlock( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.user, + suite.testData.otherUser, "reason", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), diff --git a/x/profiles/keeper/grpc_query_test.go b/x/profiles/keeper/grpc_query_test.go index 2e05d361d5..8d95d7d5ef 100644 --- a/x/profiles/keeper/grpc_query_test.go +++ b/x/profiles/keeper/grpc_query_test.go @@ -110,25 +110,25 @@ func (suite *KeeperTestSuite) Test_IncomingDTagTransferRequests() { storedRequests: []types.DTagTransferRequest{ types.NewDTagTransferRequest( "dtag", - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos19mj6dkd85m84gxvf8d929w572z5h9q0u8d8wpa", + suite.testData.user, + suite.testData.otherUser, ), types.NewDTagTransferRequest( "dtag-2", - "cosmos19mj6dkd85m84gxvf8d929w572z5h9q0u8d8wpa", - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + suite.testData.otherUser, + suite.testData.user, ), }, req: types.NewQueryIncomingDTagTransferRequestsRequest( - "cosmos19mj6dkd85m84gxvf8d929w572z5h9q0u8d8wpa", + suite.testData.otherUser, nil, ), shouldErr: false, expRequests: []types.DTagTransferRequest{ types.NewDTagTransferRequest( "dtag", - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos19mj6dkd85m84gxvf8d929w572z5h9q0u8d8wpa", + suite.testData.user, + suite.testData.otherUser, ), }, }, @@ -137,25 +137,25 @@ func (suite *KeeperTestSuite) Test_IncomingDTagTransferRequests() { storedRequests: []types.DTagTransferRequest{ types.NewDTagTransferRequest( "dtag", - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos19mj6dkd85m84gxvf8d929w572z5h9q0u8d8wpa", + suite.testData.user, + suite.testData.otherUser, ), types.NewDTagTransferRequest( "dtag-2", - "cosmos10nsdxxdvy9qka3zv0lzw8z9cnu6kanld8jh773", - "cosmos19mj6dkd85m84gxvf8d929w572z5h9q0u8d8wpa", + suite.testData.otherUser, + suite.testData.user, ), }, req: types.NewQueryIncomingDTagTransferRequestsRequest( - "cosmos19mj6dkd85m84gxvf8d929w572z5h9q0u8d8wpa", - &query.PageRequest{Limit: 1, Offset: 1, CountTotal: true}, + suite.testData.otherUser, + &query.PageRequest{Limit: 1}, ), shouldErr: false, expRequests: []types.DTagTransferRequest{ types.NewDTagTransferRequest( "dtag", - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos19mj6dkd85m84gxvf8d929w572z5h9q0u8d8wpa", + suite.testData.user, + suite.testData.otherUser, ), }, }, @@ -163,8 +163,16 @@ func (suite *KeeperTestSuite) Test_IncomingDTagTransferRequests() { for _, uc := range usecases { uc := uc + suite.SetupTest() suite.Run(uc.name, func() { - suite.SetupTest() + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) for _, req := range uc.storedRequests { suite.Require().NoError(suite.k.SaveDTagTransferRequest(suite.ctx, req)) @@ -341,7 +349,7 @@ func (suite *KeeperTestSuite) Test_UserChainLink() { link := types.NewChainLink( address, - types.NewBech32Address("cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", "cosmos"), + types.NewBech32Address("cosmos1nc54z3kzyal57w6wcf5khmwrxx5rafnwvu0m5z", "cosmos"), types.NewProof( pubKey, "909e38994b1583d3f14384c2e9a03c90064e8fd8e19b780bb0ba303dfe671a27287da04d0ce096ce9a140bd070ee36818f5519eb2070a16971efd8143855524b", @@ -355,13 +363,13 @@ func (suite *KeeperTestSuite) Test_UserChainLink() { req: &types.QueryUserChainLinkRequest{ User: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", ChainName: "cosmos", - Target: "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + Target: "cosmos1nc54z3kzyal57w6wcf5khmwrxx5rafnwvu0m5z", }, shouldErr: false, expRes: &types.QueryUserChainLinkResponse{ Link: types.NewChainLink( "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - types.NewBech32Address("cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", "cosmos"), + types.NewBech32Address("cosmos1nc54z3kzyal57w6wcf5khmwrxx5rafnwvu0m5z", "cosmos"), types.NewProof( pubKey, "909e38994b1583d3f14384c2e9a03c90064e8fd8e19b780bb0ba303dfe671a27287da04d0ce096ce9a140bd070ee36818f5519eb2070a16971efd8143855524b", @@ -404,16 +412,16 @@ func (suite *KeeperTestSuite) Test_UserRelationships() { expLen int }{ { - name: "query relationsips without pagination", + name: "query relationships without pagination", storedRelationships: []types.Relationship{ types.NewRelationship( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1s3nh6tafl4amaxkke9kdejhp09lk93g9ev39r4", + suite.testData.user, + suite.testData.otherUser, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), types.NewRelationship( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.user, + "cosmos19mj6dkd85m84gxvf8d929w572z5h9q0u8d8wpa", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), }, @@ -425,13 +433,13 @@ func (suite *KeeperTestSuite) Test_UserRelationships() { name: "query relationsips with pagination", storedRelationships: []types.Relationship{ types.NewRelationship( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1s3nh6tafl4amaxkke9kdejhp09lk93g9ev39r4", + suite.testData.user, + suite.testData.otherUser, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), types.NewRelationship( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.otherUser, + suite.testData.user, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), }, @@ -443,11 +451,21 @@ func (suite *KeeperTestSuite) Test_UserRelationships() { for _, uc := range usecases { uc := uc + suite.SetupTest() suite.Run(uc.name, func() { - suite.SetupTest() + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) for _, relationship := range uc.storedRelationships { - suite.k.SaveRelationship(suite.ctx, relationship) + err = suite.k.SaveRelationship(suite.ctx, relationship) + suite.Require().NoError(err) } res, err := suite.k.UserRelationships(sdk.WrapSDKContext(suite.ctx), uc.req) @@ -464,24 +482,24 @@ func (suite *KeeperTestSuite) Test_UserRelationships() { func (suite *KeeperTestSuite) Test_UserBlocks() { usecases := []struct { - name string + name string storedUserBlocks []types.UserBlock - req *types.QueryUserBlocksRequest - shouldErr bool - expLen int + req *types.QueryUserBlocksRequest + shouldErr bool + expLen int }{ { name: "query blocks without pagination", storedUserBlocks: []types.UserBlock{ types.NewUserBlock( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1s3nh6tafl4amaxkke9kdejhp09lk93g9ev39r4", + suite.testData.user, + suite.testData.otherUser, "reason1", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), types.NewUserBlock( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.user, + "cosmos19mj6dkd85m84gxvf8d929w572z5h9q0u8d8wpa", "reason2", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), @@ -494,14 +512,14 @@ func (suite *KeeperTestSuite) Test_UserBlocks() { name: "query blocks with pagination", storedUserBlocks: []types.UserBlock{ types.NewUserBlock( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1s3nh6tafl4amaxkke9kdejhp09lk93g9ev39r4", + suite.testData.user, + suite.testData.otherUser, "reason1", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), types.NewUserBlock( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.otherUser, + suite.testData.user, "reason2", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), @@ -514,11 +532,20 @@ func (suite *KeeperTestSuite) Test_UserBlocks() { for _, uc := range usecases { uc := uc + suite.SetupTest() suite.Run(uc.name, func() { - suite.SetupTest() + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) - for _, UserBlock := range uc.storedUserBlocks { - suite.k.SaveUserBlock(suite.ctx, UserBlock) + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + + for _, block := range uc.storedUserBlocks { + err := suite.k.SaveUserBlock(suite.ctx, block) + suite.Require().NoError(err) } res, err := suite.k.UserBlocks(sdk.WrapSDKContext(suite.ctx), uc.req) diff --git a/x/profiles/keeper/invariants.go b/x/profiles/keeper/invariants.go index d2068544f8..5df7e16301 100644 --- a/x/profiles/keeper/invariants.go +++ b/x/profiles/keeper/invariants.go @@ -10,47 +10,234 @@ import ( // RegisterInvariants registers all posts invariants func RegisterInvariants(ir sdk.InvariantRegistry, keeper Keeper) { - ir.RegisterRoute(types.ModuleName, "valid-profile", - ValidProfileInvariant(keeper)) + ir.RegisterRoute(types.ModuleName, "valid-profiles", ValidProfilesInvariant(keeper)) + ir.RegisterRoute(types.ModuleName, "valid-user-blocks", ValidUserBlocksInvariant(keeper)) + ir.RegisterRoute(types.ModuleName, "valid-relationships", ValidRelationshipsInvariant(keeper)) + ir.RegisterRoute(types.ModuleName, "valid-dtag-transfer-requests", ValidDTagTransferRequests(keeper)) + ir.RegisterRoute(types.ModuleName, "valid-chain-links", ValidChainLinks(keeper)) + ir.RegisterRoute(types.ModuleName, "valid-application-links", ValidApplicationLinks(keeper)) } func AllInvariants(k Keeper) sdk.Invariant { return func(ctx sdk.Context) (string, bool) { - if res, stop := ValidProfileInvariant(k)(ctx); stop { - return res, stop + res, broken := ValidProfilesInvariant(k)(ctx) + if broken { + return res, broken } - return "Every invariant condition is fulfilled correctly", true + res, broken = ValidUserBlocksInvariant(k)(ctx) + if broken { + return res, broken + } + + res, broken = ValidRelationshipsInvariant(k)(ctx) + if broken { + return res, broken + } + + res, broken = ValidDTagTransferRequests(k)(ctx) + if broken { + return res, broken + } + + res, broken = ValidChainLinks(k)(ctx) + if broken { + return res, broken + } + + res, broken = ValidApplicationLinks(k)(ctx) + if broken { + return res, broken + } + + return "Every invariant condition is fulfilled correctly", false + } +} + +// ValidProfilesInvariant checks that all registered Profiles have a non-empty DTag and a non-empty creator +func ValidProfilesInvariant(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + var invalidProfiles []*types.Profile + k.IterateProfiles(ctx, func(_ int64, profile *types.Profile) (stop bool) { + if err := profile.Validate(); err != nil { + invalidProfiles = append(invalidProfiles, profile) + } + return false + }) + + broken := len(invalidProfiles) != 0 + return sdk.FormatInvariant(types.ModuleName, "invalid profiles", + formatOutputProfiles(invalidProfiles)), broken } } // formatOutputProfiles prepare invalid Profiles to be displayed correctly func formatOutputProfiles(invalidProfiles []*types.Profile) (outputProfiles string) { - outputProfiles = "Invalid profiles:\n" + outputProfiles = "The following list contains invalid profiles:\n" for _, invalidProfile := range invalidProfiles { outputProfiles += fmt.Sprintf( "[DTag]: %s, [Creator]: %s\n", - invalidProfile.DTag, - invalidProfile.GetAddress().String(), + invalidProfile.DTag, invalidProfile.GetAddress().String(), ) } return outputProfiles } -// ValidProfileInvariant checks that all registered Profiles have a non-empty DTag and a non-empty creator -func ValidProfileInvariant(k Keeper) sdk.Invariant { +// -------------------------------------------------------------------------------------------------------------------- + +// ValidUserBlocksInvariant checks that all created user blocks have been created by a user with a profile +// and they do not have the same user as creator and recipient +func ValidUserBlocksInvariant(k Keeper) sdk.Invariant { return func(ctx sdk.Context) (string, bool) { - var invalidProfiles []*types.Profile - k.IterateProfiles(ctx, func(_ int64, profile *types.Profile) (stop bool) { - if err := profile.Validate(); err != nil { - invalidProfiles = append(invalidProfiles, profile) + var invalidBlocks []types.UserBlock + k.IterateBlocks(ctx, func(index int64, block types.UserBlock) (stop bool) { + if !k.HasProfile(ctx, block.Blocker) || block.Blocker == block.Blocked { + invalidBlocks = append(invalidBlocks, block) } return false }) - return sdk.FormatInvariant(types.ModuleName, "invalid profiles", - fmt.Sprintf("The following list contains invalid profiles:\n %s", - formatOutputProfiles(invalidProfiles)), - ), invalidProfiles != nil + broken := len(invalidBlocks) != 0 + return sdk.FormatInvariant(types.ModuleName, "invalid user blocks", + formatOutputBlocks(invalidBlocks)), broken + } +} + +// formatOutputProfiles prepares the given invalid user blocks to be displayed correctly +func formatOutputBlocks(invalidBlocks []types.UserBlock) (outputBlocks string) { + outputBlocks = "The following list contains invalid user blocks:\n" + for _, block := range invalidBlocks { + outputBlocks += fmt.Sprintf( + "[Blocker]: %s, [Blocked]: %s, [Subspace]: %s\n", + block.Blocker, block.Blocked, block.Subspace, + ) + } + return outputBlocks +} + +// -------------------------------------------------------------------------------------------------------------------- + +// ValidRelationshipsInvariant checks that all relationships are associated with a creator that has a profile +// and they do not have the same user as creator and recipient +func ValidRelationshipsInvariant(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + var invalidRelationships []types.Relationship + k.IterateRelationships(ctx, func(index int64, relationship types.Relationship) (stop bool) { + if !k.HasProfile(ctx, relationship.Creator) || relationship.Creator == relationship.Recipient { + invalidRelationships = append(invalidRelationships, relationship) + } + return false + }) + + broken := len(invalidRelationships) != 0 + return sdk.FormatInvariant(types.ModuleName, "invalid relationships", + formatOutputRelationships(invalidRelationships)), broken + } +} + +// formatOutputRelationships prepares the given invalid relationships to be displayed correctly +func formatOutputRelationships(relationships []types.Relationship) (output string) { + output = "The following list contains invalid relationships:\n" + for _, relationship := range relationships { + output += fmt.Sprintf( + "[Creator]: %s, [Recipient]: %s, [Subspace]: %s\n", + relationship.Creator, relationship.Recipient, relationship.Subspace, + ) + } + return output +} + +// -------------------------------------------------------------------------------------------------------------------- + +// ValidDTagTransferRequests checks that all DTag transfer requests are associated with a recipient that has a profile +// and they have not been made from the same user towards the same user +func ValidDTagTransferRequests(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + var invalidDTagTransferRequests []types.DTagTransferRequest + k.IterateDTagTransferRequests(ctx, func(index int64, request types.DTagTransferRequest) (stop bool) { + if !k.HasProfile(ctx, request.Receiver) || request.Sender == request.Receiver { + invalidDTagTransferRequests = append(invalidDTagTransferRequests, request) + } + return false + }) + + broken := len(invalidDTagTransferRequests) != 0 + return sdk.FormatInvariant(types.ModuleName, "invalid dtag transfer requests", + formatOutputDTagTransferRequests(invalidDTagTransferRequests)), broken + } +} + +// formatOutputDTagTransferRequests prepares the given invalid DTag transfer requests to be displayed correctly +func formatOutputDTagTransferRequests(requests []types.DTagTransferRequest) (output string) { + output = "The following list contains invalid DTag transfer requests:\n" + for _, request := range requests { + output += fmt.Sprintf( + "[Sender]: %s, [Receiver]: %s\n", + request.Sender, request.Receiver, + ) + } + return output +} + +// -------------------------------------------------------------------------------------------------------------------- + +// ValidChainLinks checks that all chain links are associated with a user that has a profile +func ValidChainLinks(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + var invalidChainLinks []types.ChainLink + k.IterateChainLinks(ctx, func(index int64, link types.ChainLink) (stop bool) { + if !k.HasProfile(ctx, link.User) { + invalidChainLinks = append(invalidChainLinks, link) + } + return false + }) + + broken := len(invalidChainLinks) != 0 + return sdk.FormatInvariant(types.ModuleName, "invalid chain links", + formatOutputChainLinks(invalidChainLinks)), broken + } +} + +// formatOutputChainLinks prepares the given invalid chain links to be displayed correctly +func formatOutputChainLinks(links []types.ChainLink) (output string) { + output = "The following list contains invalid chain links:\n" + for _, link := range links { + address := link.Address.GetCachedValue().(types.AddressData) + output += fmt.Sprintf( + "[User]: %s, [Chain]: %s, [Address]: %s\n", + link.User, link.ChainConfig.Name, address.GetValue(), + ) + } + return output +} + +// -------------------------------------------------------------------------------------------------------------------- + +// ValidApplicationLinks checks that all application links are associated with a user that has a profile +func ValidApplicationLinks(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + var invalidApplicationLinks []types.ApplicationLink + k.IterateApplicationLinks(ctx, func(index int64, link types.ApplicationLink) (stop bool) { + if !k.HasProfile(ctx, link.User) { + invalidApplicationLinks = append(invalidApplicationLinks, link) + } + return false + }) + + broken := len(invalidApplicationLinks) != 0 + return sdk.FormatInvariant(types.ModuleName, "invalid application links", + formatOutputApplicationLinks(invalidApplicationLinks)), broken + } +} + +// formatOutputApplicationLinks prepares the given invalid application links to be displayed correctly +func formatOutputApplicationLinks(links []types.ApplicationLink) (output string) { + output = "The following list contains invalid application links:\n" + for _, link := range links { + output += fmt.Sprintf( + "[User]: %s, [Application]: %s, [Username]: %s\n", + link.User, link.Data.Application, link.Data.Username, + ) } + return output } diff --git a/x/profiles/keeper/invariants_test.go b/x/profiles/keeper/invariants_test.go index 2354308065..d33c4c021f 100644 --- a/x/profiles/keeper/invariants_test.go +++ b/x/profiles/keeper/invariants_test.go @@ -1,8 +1,12 @@ package keeper_test import ( + "fmt" "time" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -14,42 +18,177 @@ func (suite *KeeperTestSuite) TestInvariants() { address, err := sdk.AccAddressFromBech32("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47") suite.Require().NoError(err) - tests := []struct { + testCases := []struct { name string - profile *types.Profile + store func(ctx sdk.Context) expResponse string - expBool bool + expBroken bool }{ { - name: "Invariants not violated", - profile: suite.CheckProfileNoError( - types.NewProfileFromAccount("dtag", authtypes.NewBaseAccountWithAddress(address), time.Now()), - ), + name: "Empty state does not break invariants", expResponse: "Every invariant condition is fulfilled correctly", - expBool: true, + expBroken: false, }, { - name: "ValidProfileInvariant violated", - profile: suite.CheckProfileNoError( - types.NewProfileFromAccount("", authtypes.NewBaseAccountWithAddress(address), time.Now()), + name: "ValidProfilesInvariant broken", + store: func(ctx sdk.Context) { + profile, err := types.NewProfileFromAccount( + "", + authtypes.NewBaseAccountWithAddress(address), + time.Now(), + ) + suite.Require().NoError(err) + suite.Require().NoError(suite.k.StoreProfile(ctx, profile)) + }, + expResponse: sdk.FormatInvariant(types.ModuleName, "invalid profiles", + fmt.Sprintf("%s%s", + "The following list contains invalid profiles:\n", + "[DTag]: , [Creator]: cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47\n", + ), ), - expResponse: "profiles: invalid profiles invariant\nThe following list contains invalid profiles:\n Invalid profiles:\n[DTag]: , [Creator]: cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47\n\n", - expBool: true, + expBroken: true, }, - } + { + name: "ValidUserBlocksInvariant broken", + store: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + + block := types.NewUserBlock("blocker", "blocked", "reason", "subspace") + store.Set( + types.UserBlockStoreKey(block.Blocker, block.Subspace, block.Blocked), + suite.cdc.MustMarshalBinaryBare(&block), + ) + }, + expBroken: true, + expResponse: sdk.FormatInvariant(types.ModuleName, "invalid user blocks", + fmt.Sprintf("%s%s", + "The following list contains invalid user blocks:\n", + "[Blocker]: blocker, [Blocked]: blocked, [Subspace]: subspace\n", + ), + ), + }, + { + name: "ValidRelationshipsInvariant broken", + store: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + + relationship := types.NewRelationship("creator", "recipient", "subspace") + store.Set( + types.RelationshipsStoreKey(relationship.Creator, relationship.Subspace, relationship.Recipient), + suite.cdc.MustMarshalBinaryBare(&relationship), + ) + }, + expBroken: true, + expResponse: sdk.FormatInvariant(types.ModuleName, "invalid relationships", + fmt.Sprintf("%s%s", + "The following list contains invalid relationships:\n", + "[Creator]: creator, [Recipient]: recipient, [Subspace]: subspace\n", + ), + ), + }, + { + name: "ValidDTagTransferRequests broken", + store: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + + request := types.NewDTagTransferRequest("dTag", "sender", "receiver") + store.Set( + types.DTagTransferRequestStoreKey(request.Sender, request.Receiver), + suite.cdc.MustMarshalBinaryBare(&request), + ) + }, + expBroken: true, + expResponse: sdk.FormatInvariant(types.ModuleName, "invalid dtag transfer requests", + fmt.Sprintf("%s%s", + "The following list contains invalid DTag transfer requests:\n", + "[Sender]: sender, [Receiver]: receiver\n", + ), + ), + }, + { + name: "ValidChainLinks broken", + store: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + + pubKey := `{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A6jN4EPjj8mHf722yjEaKaGdJpxnTR40pDvXlX1mni9C"}` + + var any codectypes.Any + err = suite.cdc.UnmarshalJSON([]byte(pubKey), &any) + suite.Require().NoError(err) + + var key cryptotypes.PubKey + err = suite.cdc.UnpackAny(&any, &key) + suite.Require().NoError(err) + + link := types.NewChainLink( + "user", + types.NewBech32Address("value", "prefix"), + types.NewProof(key, "signature", "value"), + types.NewChainConfig("chain_name"), + time.Now(), + ) + store.Set( + types.ChainLinksStoreKey("user", "chain_name", "address"), + suite.cdc.MustMarshalBinaryBare(&link), + ) + }, + expBroken: true, + expResponse: sdk.FormatInvariant(types.ModuleName, "invalid chain links", + fmt.Sprintf("%s%s", + "The following list contains invalid chain links:\n", + "[User]: user, [Chain]: chain_name, [Address]: value\n", + ), + ), + }, + { + name: "ValidApplicationLinks broken", + store: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + + pubKey := `{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A6jN4EPjj8mHf722yjEaKaGdJpxnTR40pDvXlX1mni9C"}` + + var any codectypes.Any + err = suite.cdc.UnmarshalJSON([]byte(pubKey), &any) + suite.Require().NoError(err) - for _, test := range tests { - test := test - suite.Run(test.name, func() { - suite.SetupTest() //reset + var key cryptotypes.PubKey + err = suite.cdc.UnpackAny(&any, &key) + suite.Require().NoError(err) - err := suite.k.StoreProfile(suite.ctx, test.profile) - suite.Require().NoError(err) + link := types.NewApplicationLink( + "user", + types.NewData("application", "username"), + types.AppLinkStateVerificationStarted, + types.NewOracleRequest(1, 1, types.NewOracleRequestCallData("", ""), "client_id"), + nil, + time.Now(), + ) + store.Set( + types.UserApplicationLinkKey("user", "application", "username"), + suite.cdc.MustMarshalBinaryBare(&link), + ) + }, + expBroken: true, + expResponse: sdk.FormatInvariant(types.ModuleName, "invalid application links", + fmt.Sprintf("%s%s", + "The following list contains invalid application links:\n", + "[User]: user, [Application]: application, [Username]: username\n", + ), + ), + }, + } - res, stop := keeper.AllInvariants(suite.k)(suite.ctx) + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } - suite.Require().Equal(test.expResponse, res) - suite.Require().Equal(test.expBool, stop) + res, broken := keeper.AllInvariants(suite.k)(ctx) + suite.Require().Equal(tc.expBroken, broken) + suite.Require().Equal(tc.expResponse, res) }) } } diff --git a/x/profiles/keeper/keeper.go b/x/profiles/keeper/keeper.go index 7bc9fb9896..3e8484cb6b 100644 --- a/x/profiles/keeper/keeper.go +++ b/x/profiles/keeper/keeper.go @@ -69,22 +69,24 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { // It assumes that the given profile has already been validated. // It returns an error if a profile with the same DTag from a different creator already exists func (k Keeper) StoreProfile(ctx sdk.Context, profile *types.Profile) error { - addr := k.GetAddressFromDTag(ctx, profile.DTag) if addr != "" && addr != profile.GetAddress().String() { return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, - "a profile with dtag %s has already been created", profile.DTag) + "a profile with DTag %s has already been created", profile.DTag) } store := ctx.KVStore(k.storeKey) - // Remove the previous DTag association (if the DTag has changed) oldProfile, found, err := k.GetProfile(ctx, profile.GetAddress().String()) if err != nil { return err } if found && oldProfile.DTag != profile.DTag { + // Remove the previous DTag association (if the DTag has changed) store.Delete(types.DTagStoreKey(oldProfile.DTag)) + + // Remove all incoming DTag transfer requests if the DTag has changed since these will be invalid now + k.DeleteAllUserIncomingDTagTransferRequests(ctx, profile.GetAddress().String()) } // Store the DTag -> Address association @@ -141,16 +143,20 @@ func (k Keeper) RemoveProfile(ctx sdk.Context, address string) error { store := ctx.KVStore(k.storeKey) store.Delete(types.DTagStoreKey(profile.DTag)) - // Delete all chains links -> Address association - k.IterateUserChainLinks(ctx, address, func(_ int64, link types.ChainLink) (stop bool) { - addrData, err := types.UnpackAddressData(k.cdc, link.Address) - if err != nil { - panic(err) - } - // It assumes that the chain link must exist - k.DeleteChainLink(ctx, address, link.ChainConfig.Name, addrData.GetAddress()) - return false - }) + // Delete all the blocks + k.DeleteAllUserBlocks(ctx, address) + + // Delete all the relationships + k.DeleteAllUserRelationships(ctx, address) + + // Delete all DTag transfer requests made towards this account + k.DeleteAllUserIncomingDTagTransferRequests(ctx, address) + + // Delete all chains links + k.DeleteAllUserChainLinks(ctx, address) + + // Delete all the application links + k.DeleteAllUserApplicationLinks(ctx, address) // Delete the profile data by replacing the stored account k.ak.SetAccount(ctx, profile.GetAccount()) diff --git a/x/profiles/keeper/keeper_app_links.go b/x/profiles/keeper/keeper_app_links.go index 84820a6c69..59ca45a92e 100644 --- a/x/profiles/keeper/keeper_app_links.go +++ b/x/profiles/keeper/keeper_app_links.go @@ -15,12 +15,7 @@ import ( // SaveApplicationLink stores the given connection replacing any existing one for the same user and application func (k Keeper) SaveApplicationLink(ctx sdk.Context, link types.ApplicationLink) error { - _, found, err := k.GetProfile(ctx, link.User) - if err != nil { - return err - } - - if !found { + if !k.HasProfile(ctx, link.User) { return sdkerrors.Wrapf(types.ErrProfileNotFound, "a profile is required to link an application") } @@ -113,3 +108,17 @@ func (k Keeper) DeleteApplicationLink(ctx sdk.Context, user string, application, return nil } + +// DeleteAllUserApplicationLinks delete all the applications links associated with the given user +func (k Keeper) DeleteAllUserApplicationLinks(ctx sdk.Context, user string) { + var links []types.ApplicationLink + k.IterateUserApplicationLinks(ctx, user, func(index int64, link types.ApplicationLink) (stop bool) { + links = append(links, link) + return false + }) + + store := ctx.KVStore(k.storeKey) + for _, link := range links { + store.Delete(types.UserApplicationLinkKey(link.User, link.Data.Application, link.Data.Username)) + } +} diff --git a/x/profiles/keeper/keeper_blocks.go b/x/profiles/keeper/keeper_blocks.go index 3bd748dc30..2bd70b686b 100644 --- a/x/profiles/keeper/keeper_blocks.go +++ b/x/profiles/keeper/keeper_blocks.go @@ -9,9 +9,20 @@ import ( // SaveUserBlock allows to store the given block inside the store, returning an error if // something goes wrong. +// It requires the blocker to have a registered profile. func (k Keeper) SaveUserBlock(ctx sdk.Context, userBlock types.UserBlock) error { + // Check the blocker to make sure they have a profile + if !k.HasProfile(ctx, userBlock.Blocker) { + return sdkerrors.Wrapf(types.ErrProfileNotFound, "blocker does not have a profile") + } + + // Check to make sure the blocker and blocked users are not the same + if userBlock.Blocker == userBlock.Blocked { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "blocker and blocked cannot be the same user") + } + store := ctx.KVStore(k.storeKey) - key := types.UsersBlocksStoreKey(userBlock.Blocker, userBlock.Subspace, userBlock.Blocked) + key := types.UserBlockStoreKey(userBlock.Blocker, userBlock.Subspace, userBlock.Blocked) if store.Has(key) { return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "the user with address %s has already been blocked", userBlock.Blocked) @@ -21,22 +32,10 @@ func (k Keeper) SaveUserBlock(ctx sdk.Context, userBlock types.UserBlock) error return nil } -// DeleteUserBlock allows to the specified blocker to unblock the given blocked user. -func (k Keeper) DeleteUserBlock(ctx sdk.Context, blocker, blocked string, subspace string) error { - store := ctx.KVStore(k.storeKey) - key := types.UsersBlocksStoreKey(blocker, subspace, blocked) - if !store.Has(key) { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, - "block from %s towards %s for subspace %s not found", blocker, blocked, subspace) - } - store.Delete(key) - return nil -} - // GetUserBlocks returns the list of users that the specified user has blocked. func (k Keeper) GetUserBlocks(ctx sdk.Context, blocker string) []types.UserBlock { var userblocks []types.UserBlock - k.IterateBlockedUsers(ctx, blocker, func(index int64, userblock types.UserBlock) (stop bool) { + k.IterateUserBlocks(ctx, blocker, func(index int64, userblock types.UserBlock) (stop bool) { userblocks = append(userblocks, userblock) return false }) @@ -69,7 +68,7 @@ func (k Keeper) IsUserBlocked(ctx sdk.Context, blocker, blocked string) bool { func (k Keeper) HasUserBlocked(ctx sdk.Context, blocker, blocked, subspace string) bool { if subspace != "" { store := ctx.KVStore(k.storeKey) - key := types.UsersBlocksStoreKey(blocker, subspace, blocked) + key := types.UserBlockStoreKey(blocker, subspace, blocked) return store.Has(key) } @@ -83,3 +82,29 @@ func (k Keeper) HasUserBlocked(ctx sdk.Context, blocker, blocked, subspace strin return false } + +// DeleteUserBlock allows to the specified blocker to unblock the given blocked user. +func (k Keeper) DeleteUserBlock(ctx sdk.Context, blocker, blocked string, subspace string) error { + store := ctx.KVStore(k.storeKey) + key := types.UserBlockStoreKey(blocker, subspace, blocked) + if !store.Has(key) { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, + "block from %s towards %s for subspace %s not found", blocker, blocked, subspace) + } + store.Delete(key) + return nil +} + +// DeleteAllUserBlocks deletes all the user blocks that have been created by the given user +func (k Keeper) DeleteAllUserBlocks(ctx sdk.Context, user string) { + var blocks []types.UserBlock + k.IterateUserBlocks(ctx, user, func(index int64, block types.UserBlock) (stop bool) { + blocks = append(blocks, block) + return false + }) + + store := ctx.KVStore(k.storeKey) + for _, block := range blocks { + store.Delete(types.UserBlockStoreKey(block.Blocker, block.Subspace, block.Blocked)) + } +} diff --git a/x/profiles/keeper/keeper_blocks_test.go b/x/profiles/keeper/keeper_blocks_test.go index 044bc52710..a2165e67aa 100644 --- a/x/profiles/keeper/keeper_blocks_test.go +++ b/x/profiles/keeper/keeper_blocks_test.go @@ -12,11 +12,11 @@ func (suite *KeeperTestSuite) TestKeeper_IsUserBlocked() { }{ { name: "blocked user found returns true", - blocker: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + blocker: suite.testData.user, blocked: "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", userBlocks: []types.UserBlock{ types.NewUserBlock( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + suite.testData.user, "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", "test", "", @@ -34,8 +34,18 @@ func (suite *KeeperTestSuite) TestKeeper_IsUserBlocked() { } for _, test := range tests { + suite.SetupTest() suite.Run(test.name, func() { - suite.SetupTest() + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + if test.userBlocks != nil { _ = suite.k.SaveUserBlock(suite.ctx, test.userBlocks[0]) } @@ -56,18 +66,18 @@ func (suite *KeeperTestSuite) TestKeeper_SaveUserBlock() { { name: "already blocked user returns error", storedUserBlocks: []types.UserBlock{ - types.NewUserBlock("user_1", "user_2", "reason", "subspace"), + types.NewUserBlock(suite.testData.user, "user_2", "reason", "subspace"), }, - userBlock: types.NewUserBlock("user_1", "user_2", "reason", "subspace"), + userBlock: types.NewUserBlock(suite.testData.user, "user_2", "reason", "subspace"), expErr: true, }, { name: "user block added correctly", storedUserBlocks: nil, - userBlock: types.NewUserBlock("user_1", "user_2", "reason", "subspace"), + userBlock: types.NewUserBlock(suite.testData.user, "user_2", "reason", "subspace"), expErr: false, expBlocks: []types.UserBlock{ - types.NewUserBlock("user_1", "user_2", "reason", "subspace"), + types.NewUserBlock(suite.testData.user, "user_2", "reason", "subspace"), }, }, } @@ -75,12 +85,18 @@ func (suite *KeeperTestSuite) TestKeeper_SaveUserBlock() { for _, test := range tests { suite.SetupTest() suite.Run(test.name, func() { + + profile := suite.CreateProfileFromAddress(suite.testData.user) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + for _, block := range test.storedUserBlocks { err := suite.k.SaveUserBlock(suite.ctx, block) suite.Require().NoError(err) } - err := suite.k.SaveUserBlock(suite.ctx, test.userBlock) + err = suite.k.SaveUserBlock(suite.ctx, test.userBlock) if test.expErr { suite.Require().Error(err) @@ -109,34 +125,34 @@ func (suite *KeeperTestSuite) TestKeeper_DeleteUserBlock() { { name: "delete user block with len(stored) > 1", storedUserBlocks: []types.UserBlock{ - types.NewUserBlock("blocker", "blocked", "reason", "subspace"), - types.NewUserBlock("blocker", "blocked_2", "reason", "subspace"), + types.NewUserBlock(suite.testData.user, "blocked", "reason", "subspace"), + types.NewUserBlock(suite.testData.user, "blocked_2", "reason", "subspace"), }, data: struct { blocker string blocked string subspace string }{ - blocker: "blocker", + blocker: suite.testData.user, blocked: "blocked", subspace: "subspace", }, expBlocks: []types.UserBlock{ - types.NewUserBlock("blocker", "blocked_2", "reason", "subspace"), + types.NewUserBlock(suite.testData.user, "blocked_2", "reason", "subspace"), }, expError: false, }, { name: "delete user block with len(stored) == 1", storedUserBlocks: []types.UserBlock{ - types.NewUserBlock("blocker", "blocked", "reason", "subspace"), + types.NewUserBlock(suite.testData.user, "blocked", "reason", "subspace"), }, data: struct { blocker string blocked string subspace string }{ - blocker: "blocker", + blocker: suite.testData.user, blocked: "blocked", subspace: "subspace", }, @@ -150,7 +166,7 @@ func (suite *KeeperTestSuite) TestKeeper_DeleteUserBlock() { blocked string subspace string }{ - blocker: "blocker", + blocker: suite.testData.user, blocked: "blocked", subspace: "subspace", }, @@ -161,12 +177,22 @@ func (suite *KeeperTestSuite) TestKeeper_DeleteUserBlock() { for _, test := range tests { suite.SetupTest() suite.Run(test.name, func() { + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + for _, block := range test.storedUserBlocks { err := suite.k.SaveUserBlock(suite.ctx, block) suite.Require().NoError(err) } - err := suite.k.DeleteUserBlock(suite.ctx, test.data.blocker, test.data.blocked, test.data.subspace) + err = suite.k.DeleteUserBlock(suite.ctx, test.data.blocker, test.data.blocked, test.data.subspace) if test.expError { suite.Require().Error(err) @@ -190,17 +216,17 @@ func (suite *KeeperTestSuite) TestKeeper_GetUserBlocks() { { name: "non empty slice is returned properly", storedUserBlocks: []types.UserBlock{ - types.NewUserBlock("blocker", "blocked", "reason", "subspace"), + types.NewUserBlock(suite.testData.user, "blocked", "reason", "subspace"), }, - user: "blocker", + user: suite.testData.user, expUserBlocks: []types.UserBlock{ - types.NewUserBlock("blocker", "blocked", "reason", "subspace"), + types.NewUserBlock(suite.testData.user, "blocked", "reason", "subspace"), }, }, { name: "empty slice is returned properly", storedUserBlocks: nil, - user: "blocker", + user: suite.testData.user, expUserBlocks: nil, }, } @@ -208,6 +234,16 @@ func (suite *KeeperTestSuite) TestKeeper_GetUserBlocks() { for _, test := range tests { suite.SetupTest() suite.Run(test.name, func() { + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + for _, block := range test.storedUserBlocks { err := suite.k.SaveUserBlock(suite.ctx, block) suite.Require().NoError(err) @@ -228,16 +264,16 @@ func (suite *KeeperTestSuite) TestKeeper_GetAllUsersBlocks() { { name: "Returns a non-empty users blocks slice", storedUsersBlocks: []types.UserBlock{ - types.NewUserBlock("user_1", "user_2", "reason", "subspace_1"), - types.NewUserBlock("user_1", "user_2", "reason", "subspace_2"), - types.NewUserBlock("user_2", "user_1", "reason", "subspace_1"), - types.NewUserBlock("user_2", "user_1", "reason", "subspace_2"), + types.NewUserBlock(suite.testData.user, "user_2", "reason", "subspace_1"), + types.NewUserBlock(suite.testData.user, "user_2", "reason", "subspace_2"), + types.NewUserBlock(suite.testData.otherUser, "user_1", "reason", "subspace_1"), + types.NewUserBlock(suite.testData.otherUser, "user_1", "reason", "subspace_2"), }, expUsersBlocks: []types.UserBlock{ - types.NewUserBlock("user_1", "user_2", "reason", "subspace_1"), - types.NewUserBlock("user_1", "user_2", "reason", "subspace_2"), - types.NewUserBlock("user_2", "user_1", "reason", "subspace_1"), - types.NewUserBlock("user_2", "user_1", "reason", "subspace_2"), + types.NewUserBlock(suite.testData.user, "user_2", "reason", "subspace_1"), + types.NewUserBlock(suite.testData.user, "user_2", "reason", "subspace_2"), + types.NewUserBlock(suite.testData.otherUser, "user_1", "reason", "subspace_1"), + types.NewUserBlock(suite.testData.otherUser, "user_1", "reason", "subspace_2"), }, }, } @@ -245,6 +281,16 @@ func (suite *KeeperTestSuite) TestKeeper_GetAllUsersBlocks() { for _, test := range tests { suite.SetupTest() suite.Run(test.name, func() { + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + for _, userBlock := range test.storedUsersBlocks { err := suite.k.SaveUserBlock(suite.ctx, userBlock) suite.Require().NoError(err) @@ -274,14 +320,14 @@ func (suite *KeeperTestSuite) TestKeeper_HasUserBlocked() { { name: "blocked user found returns true", storedBlocks: []types.UserBlock{ - types.NewUserBlock("blocker", "blocked", "reason", "subspace"), + types.NewUserBlock(suite.testData.user, "blocked", "reason", "subspace"), }, data: struct { blocker string blocked string subspace string }{ - blocker: "blocker", + blocker: suite.testData.user, blocked: "blocked", subspace: "subspace", }, @@ -290,14 +336,14 @@ func (suite *KeeperTestSuite) TestKeeper_HasUserBlocked() { { name: "blocked user not found returns false", storedBlocks: []types.UserBlock{ - types.NewUserBlock("blocker", "blocked", "reason", "subspace"), + types.NewUserBlock(suite.testData.user, "blocked", "reason", "subspace"), }, data: struct { blocker string blocked string subspace string }{ - blocker: "blocker", + blocker: suite.testData.user, blocked: "blocked", subspace: "subspace_2", }, @@ -306,8 +352,17 @@ func (suite *KeeperTestSuite) TestKeeper_HasUserBlocked() { } for _, test := range tests { + suite.SetupTest() suite.Run(test.name, func() { - suite.SetupTest() + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) for _, block := range test.storedBlocks { err := suite.k.SaveUserBlock(suite.ctx, block) diff --git a/x/profiles/keeper/keeper_chain_links.go b/x/profiles/keeper/keeper_chain_links.go index e9f331c810..10bf65eac8 100644 --- a/x/profiles/keeper/keeper_chain_links.go +++ b/x/profiles/keeper/keeper_chain_links.go @@ -9,13 +9,17 @@ import ( // SaveChainLink stores the given chain link func (k Keeper) SaveChainLink(ctx sdk.Context, link types.ChainLink) error { - // Validate the chain link err := link.Validate() if err != nil { return sdkerrors.Wrap(types.ErrInvalidChainLink, err.Error()) } + // Make sure the user has a profile + if !k.HasProfile(ctx, link.User) { + return sdkerrors.Wrap(types.ErrProfileNotFound, "a profile is required to link a chain") + } + // Validate the source address srcAddrData, err := types.UnpackAddressData(k.cdc, link.Address) if err != nil { @@ -28,26 +32,16 @@ func (k Keeper) SaveChainLink(ctx sdk.Context, link types.ChainLink) error { } // Verify the proof - err = link.Proof.Verify(k.cdc) + err = link.Proof.Verify(k.cdc, srcAddrData) if err != nil { return sdkerrors.Wrap(types.ErrInvalidProof, err.Error()) } - target := srcAddrData.GetAddress() + target := srcAddrData.GetValue() if _, found := k.GetChainLink(ctx, link.User, link.ChainConfig.Name, target); found { return types.ErrDuplicatedChainLink } - // Make sure the user has a profile - _, found, err := k.GetProfile(ctx, link.User) - if err != nil { - return err - } - - if !found { - return sdkerrors.Wrap(types.ErrProfileNotFound, "target user does not have a profile") - } - // Set chain link -> address association store := ctx.KVStore(k.storeKey) key := types.ChainLinksStoreKey(link.User, link.ChainConfig.Name, target) @@ -55,6 +49,19 @@ func (k Keeper) SaveChainLink(ctx sdk.Context, link types.ChainLink) error { return nil } +// GetChainLink returns the chain link for the given owner, chain name and target. +// If such link does not exist, returns false instead. +func (k Keeper) GetChainLink(ctx sdk.Context, owner, chainName, target string) (types.ChainLink, bool) { + store := ctx.KVStore(k.storeKey) + key := types.ChainLinksStoreKey(owner, chainName, target) + + if !store.Has(key) { + return types.ChainLink{}, false + } + + return types.MustUnmarshalChainLink(k.cdc, store.Get(key)), true +} + // DeleteChainLink deletes the link associated with the given address and chain name func (k Keeper) DeleteChainLink(ctx sdk.Context, owner, chainName, target string) error { store := ctx.KVStore(k.storeKey) @@ -69,25 +76,17 @@ func (k Keeper) DeleteChainLink(ctx sdk.Context, owner, chainName, target string return nil } -// GetChainLink returns the chain link for the given owner, chain name and target. -// If such link does not exist, returns false instead. -func (k Keeper) GetChainLink(ctx sdk.Context, owner, chainName, target string) (types.ChainLink, bool) { - store := ctx.KVStore(k.storeKey) - key := types.ChainLinksStoreKey(owner, chainName, target) - - if !store.Has(key) { - return types.ChainLink{}, false - } - - return types.MustUnmarshalChainLink(k.cdc, store.Get(key)), true -} - -// GetChainLinks allows to returns the list of all stored chain links -func (k Keeper) GetChainLinks(ctx sdk.Context) []types.ChainLink { +// DeleteAllUserChainLinks deletes all the chain links associated with the given user +func (k Keeper) DeleteAllUserChainLinks(ctx sdk.Context, user string) { var links []types.ChainLink - k.IterateChainLinks(ctx, func(_ int64, link types.ChainLink) (stop bool) { + k.IterateUserChainLinks(ctx, user, func(index int64, link types.ChainLink) (stop bool) { links = append(links, link) return false }) - return links + + store := ctx.KVStore(k.storeKey) + for _, link := range links { + address := link.Address.GetCachedValue().(types.AddressData) + store.Delete(types.ChainLinksStoreKey(link.User, link.ChainConfig.Name, address.GetValue())) + } } diff --git a/x/profiles/keeper/keeper_chain_links_test.go b/x/profiles/keeper/keeper_chain_links_test.go index 249c1add1b..6ce2f21f7c 100644 --- a/x/profiles/keeper/keeper_chain_links_test.go +++ b/x/profiles/keeper/keeper_chain_links_test.go @@ -4,6 +4,8 @@ import ( "encoding/hex" "time" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" sdk "github.com/cosmos/cosmos-sdk/types" @@ -165,6 +167,60 @@ func (suite *KeeperTestSuite) TestKeeper_StoreChainLink() { } } +func (suite *KeeperTestSuite) TestKeeper_GetChainLink() { + tests := []struct { + name string + store func() + owner string + chainName string + address string + shouldFound bool + }{ + { + name: "Non existent link returns anything", + owner: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + chainName: "cosmos", + address: "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + shouldFound: false, + }, + { + name: "Existent link returns no error", + store: func() { + store := suite.ctx.KVStore(suite.storeKey) + key := types.ChainLinksStoreKey("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", "cosmos", suite.testData.user) + link := types.NewChainLink( + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + types.NewBech32Address("cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", "cosmos"), + types.NewProof(secp256k1.GenPrivKey().PubKey(), "signature", "plain_text"), + types.NewChainConfig("cosmos"), + time.Date(2020, 1, 2, 00, 00, 00, 000, time.UTC), + ) + store.Set(key, types.MustMarshalChainLink(suite.cdc, link)) + }, + owner: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + chainName: "cosmos", + address: suite.testData.user, + shouldFound: true, + }, + } + + for _, test := range tests { + suite.Run(test.name, func() { + suite.SetupTest() + if test.store != nil { + test.store() + } + + _, found := suite.k.GetChainLink(suite.ctx, test.owner, test.chainName, test.address) + if test.shouldFound { + suite.Require().True(found) + } else { + suite.Require().False(found) + } + }) + } +} + func (suite *KeeperTestSuite) TestKeeper_DeleteChainLink() { profileAcc, err := sdk.AccAddressFromBech32(suite.testData.user) suite.Require().NoError(err) @@ -242,133 +298,90 @@ func (suite *KeeperTestSuite) TestKeeper_DeleteChainLink() { } } -func (suite *KeeperTestSuite) TestKeeper_GetChainLink() { - tests := []struct { - name string - store func() - owner string - chainName string - address string - shouldFound bool +func (suite *KeeperTestSuite) TestKeeper_DeleteAllUserChainLinks() { + testCases := []struct { + name string + store func(ctx sdk.Context) + user string + check func(ctx sdk.Context) }{ { - name: "Non existent link returns anything", - owner: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - chainName: "cosmos", - address: "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", - shouldFound: false, - }, - { - name: "Existent link returns no error", - store: func() { - store := suite.ctx.KVStore(suite.storeKey) - key := types.ChainLinksStoreKey("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", "cosmos", suite.testData.user) - link := types.NewChainLink( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - types.NewBech32Address("cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", "cosmos"), - types.NewProof(secp256k1.GenPrivKey().PubKey(), "signature", "plain_text"), - types.NewChainConfig("cosmos"), - time.Date(2020, 1, 2, 00, 00, 00, 000, time.UTC), - ) - store.Set(key, types.MustMarshalChainLink(suite.cdc, link)) + name: "empty links are deleted properly", + user: "cosmos10nsdxxdvy9qka3zv0lzw8z9cnu6kanld8jh773", + check: func(ctx sdk.Context) { + address := "cosmos10nsdxxdvy9qka3zv0lzw8z9cnu6kanld8jh773" + var iterations = 0 + suite.k.IterateUserChainLinks(ctx, address, func(index int64, link types.ChainLink) (stop bool) { + iterations++ + return false + }) + suite.Require().Zero(iterations) }, - owner: "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - chainName: "cosmos", - address: suite.testData.user, - shouldFound: true, }, - } - - for _, test := range tests { - suite.Run(test.name, func() { - suite.SetupTest() - if test.store != nil { - test.store() - } + { + name: "existing chain links are deleted properly", + store: func(ctx sdk.Context) { + pubKey := `{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A6jN4EPjj8mHf722yjEaKaGdJpxnTR40pDvXlX1mni9C"}` - _, found := suite.k.GetChainLink(suite.ctx, test.owner, test.chainName, test.address) - if test.shouldFound { - suite.Require().True(found) - } else { - suite.Require().False(found) - } - }) - } -} + var any codectypes.Any + err := suite.cdc.UnmarshalJSON([]byte(pubKey), &any) + suite.Require().NoError(err) -func (suite *KeeperTestSuite) TestKeeper_GetAllChainLink() { - pub1 := secp256k1.GenPrivKey().PubKey() - pub2 := secp256k1.GenPrivKey().PubKey() + var key cryptotypes.PubKey + err = suite.cdc.UnpackAny(&any, &key) + suite.Require().NoError(err) - tests := []struct { - name string - store func() - expStored []types.ChainLink - }{ - { - name: "Non existent link returns empty array", - expStored: []types.ChainLink{}, - }, - { - name: "Existent links returns all links", - store: func() { - store := suite.ctx.KVStore(suite.storeKey) + store := ctx.KVStore(suite.storeKey) + user := "cosmos19xz3mrvzvp9ymgmudhpukucg6668l5haakh04x" + link := types.NewChainLink( + user, + types.NewBech32Address("cosmos10nsdxxdvy9qka3zv0lzw8z9cnu6kanld8jh773", "cosmos"), + types.NewProof(key, "signature", "plain text"), + types.NewChainConfig("cosmos"), + time.Date(2021, 1, 1, 00, 00, 00, 000, time.UTC), + ) store.Set( - types.ChainLinksStoreKey("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", "cosmos", "cosmos10clxpupsmddtj7wu7g0wdysajqwp890mva046f"), - types.MustMarshalChainLink( - suite.cdc, - types.NewChainLink( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - types.NewBech32Address("cosmos10clxpupsmddtj7wu7g0wdysajqwp890mva046f", "cosmos"), - types.NewProof(pub1, "signature", "plain_text"), - types.NewChainConfig("cosmos"), - time.Date(2020, 1, 2, 00, 00, 00, 000, time.UTC), - ), - ), + types.ChainLinksStoreKey(link.User, link.ChainConfig.Name, link.GetAddressData().GetValue()), + suite.cdc.MustMarshalBinaryBare(&link), + ) + + link = types.NewChainLink( + user, + types.NewBech32Address("cosmos1xcy3els9ua75kdm783c3qu0rfa2eplesldfevn", "cosmos"), + types.NewProof(key, "signature", "plain text"), + types.NewChainConfig("cosmos"), + time.Date(2021, 1, 1, 00, 00, 00, 000, time.UTC), ) store.Set( - types.ChainLinksStoreKey("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", "cosmos", "cosmos1ftkjv8njvkekk00ehwdfl5sst8zgdpenjfm4hs"), - types.MustMarshalChainLink( - suite.cdc, - types.NewChainLink( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - types.NewBech32Address("cosmos1ftkjv8njvkekk00ehwdfl5sst8zgdpenjfm4hs", "cosmos"), - types.NewProof(pub2, "signature", "plain_text"), - types.NewChainConfig("cosmos"), - time.Date(2020, 1, 2, 00, 00, 00, 000, time.UTC), - ), - ), + types.ChainLinksStoreKey(link.User, link.ChainConfig.Name, link.GetAddressData().GetValue()), + suite.cdc.MustMarshalBinaryBare(&link), ) }, - expStored: []types.ChainLink{ - types.NewChainLink( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - types.NewBech32Address("cosmos10clxpupsmddtj7wu7g0wdysajqwp890mva046f", "cosmos"), - types.NewProof(pub1, "signature", "plain_text"), - types.NewChainConfig("cosmos"), - time.Date(2020, 1, 2, 00, 00, 00, 000, time.UTC), - ), - types.NewChainLink( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - types.NewBech32Address("cosmos1ftkjv8njvkekk00ehwdfl5sst8zgdpenjfm4hs", "cosmos"), - types.NewProof(pub2, "signature", "plain_text"), - types.NewChainConfig("cosmos"), - time.Date(2020, 1, 2, 00, 00, 00, 000, time.UTC), - ), + user: "cosmos19xz3mrvzvp9ymgmudhpukucg6668l5haakh04x", + check: func(ctx sdk.Context) { + address := "cosmos10nsdxxdvy9qka3zv0lzw8z9cnu6kanld8jh773" + var iterations = 0 + suite.k.IterateUserChainLinks(ctx, address, func(index int64, link types.ChainLink) (stop bool) { + iterations++ + return false + }) + suite.Require().Zero(iterations) }, }, } - for _, test := range tests { - suite.Run(test.name, func() { - suite.SetupTest() - if test.store != nil { - test.store() + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) } - links := suite.k.GetChainLinks(suite.ctx) - suite.Require().Equal(len(test.expStored), len(links)) - for _, link := range links { - suite.Require().Contains(test.expStored, link) + + suite.k.DeleteAllUserChainLinks(ctx, tc.user) + + if tc.check != nil { + tc.check(ctx) } }) } diff --git a/x/profiles/keeper/keeper_dtag_transfers.go b/x/profiles/keeper/keeper_dtag_transfers.go index 726371d4de..63cb818668 100644 --- a/x/profiles/keeper/keeper_dtag_transfers.go +++ b/x/profiles/keeper/keeper_dtag_transfers.go @@ -9,7 +9,13 @@ import ( // SaveDTagTransferRequest save the given request associating it to the request recipient. // It returns an error if the same request already exists. +// It requires that the request recipient has already a profile. func (k Keeper) SaveDTagTransferRequest(ctx sdk.Context, request types.DTagTransferRequest) error { + // Check the recipient to make sure they have a profile + if !k.HasProfile(ctx, request.Receiver) { + return sdkerrors.Wrap(types.ErrProfileNotFound, "request receiver does not have a profile") + } + store := ctx.KVStore(k.storeKey) key := types.DTagTransferRequestStoreKey(request.Sender, request.Receiver) if store.Has(key) { @@ -67,8 +73,8 @@ func (k Keeper) DeleteDTagTransferRequest(ctx sdk.Context, sender, recipient str return nil } -// DeleteAllDTagTransferRequests delete all the requests made to the given user -func (k Keeper) DeleteAllDTagTransferRequests(ctx sdk.Context, receiver string) { +// DeleteAllUserIncomingDTagTransferRequests deletes all the requests made to the given user +func (k Keeper) DeleteAllUserIncomingDTagTransferRequests(ctx sdk.Context, receiver string) { var requests []types.DTagTransferRequest k.IterateUserIncomingDTagTransferRequests(ctx, receiver, func(index int64, request types.DTagTransferRequest) (stop bool) { requests = append(requests, request) diff --git a/x/profiles/keeper/keeper_dtag_transfers_test.go b/x/profiles/keeper/keeper_dtag_transfers_test.go index 8acc54ed9c..141d5ef957 100644 --- a/x/profiles/keeper/keeper_dtag_transfers_test.go +++ b/x/profiles/keeper/keeper_dtag_transfers_test.go @@ -1,6 +1,8 @@ package keeper_test import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/desmos-labs/desmos/x/profiles/types" ) @@ -72,12 +74,22 @@ func (suite *KeeperTestSuite) TestKeeper_SaveDTagTransferRequest() { for _, test := range tests { suite.SetupTest() suite.Run(test.name, func() { + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + for _, req := range test.storedTransferReqs { err := suite.k.SaveDTagTransferRequest(suite.ctx, req) suite.Require().NoError(err) } - err := suite.k.SaveDTagTransferRequest(suite.ctx, test.transferReq) + err = suite.k.SaveDTagTransferRequest(suite.ctx, test.transferReq) if test.shouldErr { suite.Require().Error(err) @@ -119,6 +131,16 @@ func (suite *KeeperTestSuite) TestKeeper_GetDTagTransferRequest() { for _, test := range tests { suite.SetupTest() suite.Run(test.name, func() { + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + for _, req := range test.storedReqs { err := suite.k.SaveDTagTransferRequest(suite.ctx, req) suite.Require().NoError(err) @@ -161,41 +183,21 @@ func (suite *KeeperTestSuite) TestKeeper_GetDTagTransferRequests() { for _, test := range tests { suite.SetupTest() suite.Run(test.name, func() { - for _, req := range test.storedReqs { - err := suite.k.SaveDTagTransferRequest(suite.ctx, req) - suite.Require().NoError(err) - } - suite.Require().Equal(test.expReqs, suite.k.GetDTagTransferRequests(suite.ctx)) - }) - } -} + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) -func (suite *KeeperTestSuite) TestKeeper_DeleteAllDTagTransferRequests() { - tests := []struct { - name string - storedReqs []types.DTagTransferRequest - expReqs []types.DTagTransferRequest - }{ - { - name: "returns a non-empty array of dTag requests", - storedReqs: []types.DTagTransferRequest{ - types.NewDTagTransferRequest("dtag", suite.testData.user, suite.testData.otherUser), - }, - expReqs: nil, - }, - } + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) - for _, test := range tests { - suite.SetupTest() - suite.Run(test.name, func() { - suite.SetupTest() for _, req := range test.storedReqs { err := suite.k.SaveDTagTransferRequest(suite.ctx, req) suite.Require().NoError(err) } - suite.k.DeleteAllDTagTransferRequests(suite.ctx, suite.testData.otherUser) suite.Require().Equal(test.expReqs, suite.k.GetDTagTransferRequests(suite.ctx)) }) } @@ -255,12 +257,22 @@ func (suite *KeeperTestSuite) TestKeeper_DeleteDTagTransferRequest() { for _, test := range tests { suite.SetupTest() suite.Run(test.name, func() { + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + for _, req := range test.storedReqs { err := suite.k.SaveDTagTransferRequest(suite.ctx, req) suite.Require().NoError(err) } - err := suite.k.DeleteDTagTransferRequest(suite.ctx, test.sender, test.receiver) + err = suite.k.DeleteDTagTransferRequest(suite.ctx, test.sender, test.receiver) if test.shouldErr { suite.Require().Error(err) @@ -273,3 +285,52 @@ func (suite *KeeperTestSuite) TestKeeper_DeleteDTagTransferRequest() { }) } } + +func (suite *KeeperTestSuite) TestKeeper_DeleteAllUserIncomingDTagTransferRequests() { + tests := []struct { + name string + store func(ctx sdk.Context) + user string + check func(ctx sdk.Context) + }{ + { + name: "DTag requests are deleted properly", + store: func(ctx sdk.Context) { + profile1 := suite.CreateProfileFromAddress("cosmos10nsdxxdvy9qka3zv0lzw8z9cnu6kanld8jh773") + suite.Require().NoError(suite.k.StoreProfile(ctx, profile1)) + + profile2 := suite.CreateProfileFromAddress("cosmos19xz3mrvzvp9ymgmudhpukucg6668l5haakh04x") + suite.Require().NoError(suite.k.StoreProfile(ctx, profile2)) + + request := types.NewDTagTransferRequest(profile1.DTag, profile2.GetAddress().String(), profile1.GetAddress().String()) + suite.Require().NoError(suite.k.SaveDTagTransferRequest(ctx, request)) + }, + user: "cosmos10nsdxxdvy9qka3zv0lzw8z9cnu6kanld8jh773", + check: func(ctx sdk.Context) { + user := "cosmos10nsdxxdvy9qka3zv0lzw8z9cnu6kanld8jh773" + + var iterations = 0 + suite.k.IterateUserIncomingDTagTransferRequests(ctx, user, func(_ int64, _ types.DTagTransferRequest) (stop bool) { + iterations += 1 + return false + }) + suite.Require().Zero(iterations) + }, + }, + } + + for _, test := range tests { + suite.SetupTest() + suite.Run(test.name, func() { + ctx, _ := suite.ctx.CacheContext() + if test.store != nil { + test.store(ctx) + } + + suite.k.DeleteAllUserIncomingDTagTransferRequests(ctx, test.user) + if test.check != nil { + test.check(ctx) + } + }) + } +} diff --git a/x/profiles/keeper/keeper_relationships.go b/x/profiles/keeper/keeper_relationships.go index 1ceba39316..75f236620c 100644 --- a/x/profiles/keeper/keeper_relationships.go +++ b/x/profiles/keeper/keeper_relationships.go @@ -8,7 +8,18 @@ import ( ) // SaveRelationship allows to store the given relationship returning an error if he's already present. +// It requires the creator to have a profile. func (k Keeper) SaveRelationship(ctx sdk.Context, relationship types.Relationship) error { + // Check the creator to make sure they have a profile + if !k.HasProfile(ctx, relationship.Creator) { + return sdkerrors.Wrap(types.ErrProfileNotFound, "relationship creator does not have a profile") + } + + // Check to make sure the creator and recipient are not the same + if relationship.Creator == relationship.Recipient { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "relationship creator and recipient cannot be the same user") + } + store := ctx.KVStore(k.storeKey) key := types.RelationshipsStoreKey(relationship.Creator, relationship.Subspace, relationship.Recipient) @@ -20,6 +31,7 @@ func (k Keeper) SaveRelationship(ctx sdk.Context, relationship types.Relationshi return nil } +// GetRelationship returns the relationship existing between the provided creator and recipient inside the given subspace func (k Keeper) GetRelationship(ctx sdk.Context, creator, subspace, recipient string) (types.Relationship, bool) { store := ctx.KVStore(k.storeKey) key := types.RelationshipsStoreKey(creator, subspace, recipient) @@ -63,3 +75,17 @@ func (k Keeper) RemoveRelationship(ctx sdk.Context, relationship types.Relations store.Delete(key) return nil } + +// DeleteAllUserRelationships removes all the relationships that somehow involve the given user +func (k Keeper) DeleteAllUserRelationships(ctx sdk.Context, user string) { + var relationships []types.Relationship + k.IterateUserRelationships(ctx, user, func(index int64, relationship types.Relationship) (stop bool) { + relationships = append(relationships, relationship) + return false + }) + + store := ctx.KVStore(k.storeKey) + for _, relationship := range relationships { + store.Delete(types.RelationshipsStoreKey(relationship.Creator, relationship.Subspace, relationship.Recipient)) + } +} diff --git a/x/profiles/keeper/keeper_relationships_test.go b/x/profiles/keeper/keeper_relationships_test.go index 48614f1f95..44bbc3817d 100644 --- a/x/profiles/keeper/keeper_relationships_test.go +++ b/x/profiles/keeper/keeper_relationships_test.go @@ -14,38 +14,38 @@ func (suite *KeeperTestSuite) TestKeeper_SaveRelationship() { { name: "already existent relationship returns error", stored: []types.Relationship{ - types.NewRelationship("user", "recipient", "subspace"), + types.NewRelationship(suite.testData.user, "recipient", "subspace"), }, - user: "user", - relationship: types.NewRelationship("user", "recipient", "subspace"), + user: suite.testData.user, + relationship: types.NewRelationship(suite.testData.user, "recipient", "subspace"), expErr: true, }, { name: "relationship added correctly", stored: nil, - user: "user", - relationship: types.NewRelationship("user", "recipient", "subspace"), + user: suite.testData.user, + relationship: types.NewRelationship(suite.testData.user, "recipient", "subspace"), expErr: false, expRelationships: []types.Relationship{ - types.NewRelationship("user", "recipient", "subspace"), + types.NewRelationship(suite.testData.user, "recipient", "subspace"), }, }, { name: "relationship added correctly (another subspace)", stored: []types.Relationship{ - types.NewRelationship("user", "recipient", "subspace"), + types.NewRelationship(suite.testData.user, "recipient", "subspace"), }, - user: "user", - relationship: types.NewRelationship("user", "recipient", "subspace_2"), + user: suite.testData.user, + relationship: types.NewRelationship(suite.testData.user, "recipient", "subspace_2"), expErr: false, }, { name: "relationship added correctly (another receiver)", stored: []types.Relationship{ - types.NewRelationship("user", "recipient", "subspace"), + types.NewRelationship(suite.testData.user, "recipient", "subspace"), }, - user: "user", - relationship: types.NewRelationship("user", "user", "subspace"), + user: suite.testData.user, + relationship: types.NewRelationship(suite.testData.user, "user", "subspace"), expErr: false, }, } @@ -53,12 +53,21 @@ func (suite *KeeperTestSuite) TestKeeper_SaveRelationship() { for _, test := range tests { suite.SetupTest() suite.Run(test.name, func() { + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + for _, relationship := range test.stored { err := suite.k.SaveRelationship(suite.ctx, relationship) suite.Require().NoError(err) } - err := suite.k.SaveRelationship(suite.ctx, test.relationship) + err = suite.k.SaveRelationship(suite.ctx, test.relationship) if test.expErr { suite.Require().Error(err) @@ -78,16 +87,16 @@ func (suite *KeeperTestSuite) TestKeeper_GetAllRelationships() { { name: "non empty relationships slice is returned properly", stored: []types.Relationship{ - types.NewRelationship("creator", "recipient", "subspace"), - types.NewRelationship("creator", "another_recipient", "subspace"), - types.NewRelationship("recipient", "creator", "subspace"), - types.NewRelationship("recipient", "creator", "subspace_2"), + types.NewRelationship(suite.testData.user, "recipient", "subspace"), + types.NewRelationship(suite.testData.user, "another_recipient", "subspace"), + types.NewRelationship(suite.testData.otherUser, "creator", "subspace"), + types.NewRelationship(suite.testData.otherUser, "creator", "subspace_2"), }, expected: []types.Relationship{ - types.NewRelationship("creator", "recipient", "subspace"), - types.NewRelationship("creator", "another_recipient", "subspace"), - types.NewRelationship("recipient", "creator", "subspace"), - types.NewRelationship("recipient", "creator", "subspace_2"), + types.NewRelationship(suite.testData.user, "recipient", "subspace"), + types.NewRelationship(suite.testData.user, "another_recipient", "subspace"), + types.NewRelationship(suite.testData.otherUser, "creator", "subspace"), + types.NewRelationship(suite.testData.otherUser, "creator", "subspace_2"), }, }, { @@ -100,6 +109,16 @@ func (suite *KeeperTestSuite) TestKeeper_GetAllRelationships() { for _, test := range tests { suite.SetupTest() suite.Run(test.name, func() { + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + for _, rel := range test.stored { err := suite.k.SaveRelationship(suite.ctx, rel) suite.Require().NoError(err) @@ -126,12 +145,12 @@ func (suite *KeeperTestSuite) TestKeeper_GetUserRelationships() { { name: "Returns non empty relationships slice", stored: []types.Relationship{ - types.NewRelationship("user_1", "user_2", "subspace"), - types.NewRelationship("user_2", "user_1", "subspace"), + types.NewRelationship(suite.testData.user, "user_2", "subspace"), + types.NewRelationship(suite.testData.otherUser, "user_1", "subspace"), }, - user: "user_1", + user: suite.testData.user, expected: []types.Relationship{ - types.NewRelationship("user_1", "user_2", "subspace"), + types.NewRelationship(suite.testData.user, "user_2", "subspace"), }, }, { @@ -144,6 +163,16 @@ func (suite *KeeperTestSuite) TestKeeper_GetUserRelationships() { for _, test := range tests { suite.SetupTest() suite.Run(test.name, func() { + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + for _, rel := range test.stored { err := suite.k.SaveRelationship(suite.ctx, rel) suite.Require().NoError(err) @@ -166,29 +195,29 @@ func (suite *KeeperTestSuite) TestKeeper_DeleteRelationship() { { name: "delete a relationship with len(relationships) > 1", stored: []types.Relationship{ - types.NewRelationship("user_1", "user_2", "subspace"), - types.NewRelationship("user_2", "user_3", "subspace"), - types.NewRelationship("user_1", "user_3", "subspace"), + types.NewRelationship(suite.testData.user, suite.testData.otherUser, "subspace"), + types.NewRelationship(suite.testData.otherUser, "user_3", "subspace"), + types.NewRelationship(suite.testData.user, "user_3", "subspace"), }, - relationshipToDelete: types.NewRelationship("user_1", "user_3", "subspace"), + relationshipToDelete: types.NewRelationship(suite.testData.otherUser, "user_3", "subspace"), expErr: false, expRelationships: []types.Relationship{ - types.NewRelationship("user_1", "user_2", "subspace"), - types.NewRelationship("user_2", "user_3", "subspace"), + types.NewRelationship(suite.testData.user, suite.testData.otherUser, "subspace"), + types.NewRelationship(suite.testData.user, "user_3", "subspace"), }, }, { name: "delete a relationship with len(relationships) == 1", stored: []types.Relationship{ - types.NewRelationship("user_3", "user_2", "subspace"), + types.NewRelationship(suite.testData.user, "user_2", "subspace"), }, - relationshipToDelete: types.NewRelationship("user_3", "user_2", "subspace"), + relationshipToDelete: types.NewRelationship(suite.testData.user, "user_2", "subspace"), expErr: false, }, { name: "deleting a non existing relationship returns an error", stored: nil, - relationshipToDelete: types.NewRelationship("user_3", "user_2", "subspace"), + relationshipToDelete: types.NewRelationship(suite.testData.user, "user_2", "subspace"), expErr: true, }, } @@ -196,12 +225,22 @@ func (suite *KeeperTestSuite) TestKeeper_DeleteRelationship() { for _, test := range tests { suite.SetupTest() suite.Run(test.name, func() { + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + for _, rel := range test.stored { err := suite.k.SaveRelationship(suite.ctx, rel) suite.Require().NoError(err) } - err := suite.k.RemoveRelationship(suite.ctx, test.relationshipToDelete) + err = suite.k.RemoveRelationship(suite.ctx, test.relationshipToDelete) if test.expErr { suite.Require().Error(err) diff --git a/x/profiles/keeper/keeper_test.go b/x/profiles/keeper/keeper_test.go index dbe94be300..1febda16e6 100644 --- a/x/profiles/keeper/keeper_test.go +++ b/x/profiles/keeper/keeper_test.go @@ -95,9 +95,13 @@ func (suite *KeeperTestSuite) TestKeeper_StoreProfile() { } func (suite *KeeperTestSuite) TestKeeper_StoreProfile_Update() { - // Store the initial profile + // Store the initial profile and some transfer requests suite.Require().NoError(suite.k.StoreProfile(suite.ctx, suite.testData.profile.Profile)) + requester := suite.CreateProfileFromAddress("cosmos1xcy3els9ua75kdm783c3qu0rfa2eplesldfevn") + request := types.NewDTagTransferRequest(suite.testData.profile.Profile.DTag, requester.GetAddress().String(), suite.testData.profile.GetAddress().String()) + suite.Require().NoError(suite.k.SaveDTagTransferRequest(suite.ctx, request)) + // Verify the store keys store := suite.ctx.KVStore(suite.storeKey) suite.Require().Equal( @@ -132,6 +136,14 @@ func (suite *KeeperTestSuite) TestKeeper_StoreProfile_Update() { for _, account := range newAccounts { suite.Require().NotContains(oldAccounts, account) } + + // Make sure the DTag transfer request is deleted since the DTag has changed + var iterations = 0 + suite.k.IterateUserIncomingDTagTransferRequests(suite.ctx, suite.testData.profile.GetAddress().String(), func(_ int64, dTagTransferRequest types.DTagTransferRequest) (stop bool) { + iterations++ + return false + }) + suite.Require().Zero(iterations) } func (suite *KeeperTestSuite) TestKeeper_GetProfile() { diff --git a/x/profiles/keeper/msg_server_blocks_test.go b/x/profiles/keeper/msg_server_blocks_test.go index 98f8d7f65c..e4b3d0a581 100644 --- a/x/profiles/keeper/msg_server_blocks_test.go +++ b/x/profiles/keeper/msg_server_blocks_test.go @@ -17,18 +17,18 @@ func (suite *KeeperTestSuite) Test_handleMsgBlockUser() { expBlocks []types.UserBlock }{ { - name: "Existing relationship returns error", + name: "Existing block returns error", stored: []types.UserBlock{ types.NewUserBlock( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.user, + suite.testData.otherUser, "reason", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), }, msg: types.NewMsgBlockUser( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.user, + suite.testData.otherUser, "reason", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), @@ -38,8 +38,8 @@ func (suite *KeeperTestSuite) Test_handleMsgBlockUser() { name: "Block has been saved correctly", stored: nil, msg: types.NewMsgBlockUser( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.user, + suite.testData.otherUser, "reason", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), @@ -47,16 +47,16 @@ func (suite *KeeperTestSuite) Test_handleMsgBlockUser() { expEvents: sdk.Events{ sdk.NewEvent( types.EventTypeBlockUser, - sdk.NewAttribute(types.AttributeKeyUserBlockBlocker, "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"), - sdk.NewAttribute(types.AttributeKeyUserBlockBlocked, "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns"), + sdk.NewAttribute(types.AttributeKeyUserBlockBlocker, suite.testData.user), + sdk.NewAttribute(types.AttributeKeyUserBlockBlocked, suite.testData.otherUser), sdk.NewAttribute(types.AttributeKeySubspace, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e"), sdk.NewAttribute(types.AttributeKeyUserBlockReason, "reason"), ), }, expBlocks: []types.UserBlock{ types.NewUserBlock( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.user, + suite.testData.otherUser, "reason", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), @@ -67,13 +67,23 @@ func (suite *KeeperTestSuite) Test_handleMsgBlockUser() { for _, test := range tests { suite.SetupTest() suite.Run(test.name, func() { + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + for _, block := range test.stored { err := suite.k.SaveUserBlock(suite.ctx, block) suite.Require().NoError(err) } service := keeper.NewMsgServerImpl(suite.k) - _, err := service.BlockUser(sdk.WrapSDKContext(suite.ctx), test.msg) + _, err = service.BlockUser(sdk.WrapSDKContext(suite.ctx), test.msg) if test.expErr { suite.Error(err) @@ -100,20 +110,20 @@ func (suite *KeeperTestSuite) Test_handleMsgUnblockUser() { { name: "Invalid block returns error", storedBlock: []types.UserBlock{}, - msg: types.NewMsgUnblockUser("blocker", "blocked", "subspace"), + msg: types.NewMsgUnblockUser(suite.testData.user, "blocked", "subspace"), expErr: true, }, { name: "Existing block is removed and leaves empty array", storedBlock: []types.UserBlock{ - types.NewUserBlock("blocker", "blocked", "reason", "subspace"), + types.NewUserBlock(suite.testData.user, "blocked", "reason", "subspace"), }, - msg: types.NewMsgUnblockUser("blocker", "blocked", "subspace"), + msg: types.NewMsgUnblockUser(suite.testData.user, "blocked", "subspace"), expErr: false, expEvents: sdk.Events{ sdk.NewEvent( types.EventTypeUnblockUser, - sdk.NewAttribute(types.AttributeKeyUserBlockBlocker, "blocker"), + sdk.NewAttribute(types.AttributeKeyUserBlockBlocker, suite.testData.user), sdk.NewAttribute(types.AttributeKeyUserBlockBlocked, "blocked"), sdk.NewAttribute(types.AttributeKeySubspace, "subspace"), ), @@ -123,29 +133,38 @@ func (suite *KeeperTestSuite) Test_handleMsgUnblockUser() { { name: "Existing block is removed and leaves non empty array", storedBlock: []types.UserBlock{ - types.NewUserBlock("blocker", "blocked", "reason", "subspace"), - types.NewUserBlock("blocker", "blocked", "reason", "other_subspace"), + types.NewUserBlock(suite.testData.user, "blocked", "reason", "subspace"), + types.NewUserBlock(suite.testData.otherUser, "blocked", "reason", "other_subspace"), }, - msg: types.NewMsgUnblockUser("blocker", "blocked", "subspace"), + msg: types.NewMsgUnblockUser(suite.testData.user, "blocked", "subspace"), expErr: false, expEvents: sdk.Events{ sdk.NewEvent( types.EventTypeUnblockUser, - sdk.NewAttribute(types.AttributeKeyUserBlockBlocker, "blocker"), + sdk.NewAttribute(types.AttributeKeyUserBlockBlocker, suite.testData.user), sdk.NewAttribute(types.AttributeKeyUserBlockBlocked, "blocked"), sdk.NewAttribute(types.AttributeKeySubspace, "subspace"), ), }, expBlocks: []types.UserBlock{ - types.NewUserBlock("blocker", "blocked", "reason", "other_subspace"), + types.NewUserBlock(suite.testData.otherUser, "blocked", "reason", "other_subspace"), }, }, } for _, test := range tests { test := test + suite.SetupTest() suite.Run(test.name, func() { - suite.SetupTest() + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) for _, block := range test.storedBlock { err := suite.k.SaveUserBlock(suite.ctx, block) @@ -153,7 +172,7 @@ func (suite *KeeperTestSuite) Test_handleMsgUnblockUser() { } service := keeper.NewMsgServerImpl(suite.k) - _, err := service.UnblockUser(sdk.WrapSDKContext(suite.ctx), test.msg) + _, err = service.UnblockUser(sdk.WrapSDKContext(suite.ctx), test.msg) if test.expErr { suite.Require().Error(err) diff --git a/x/profiles/keeper/msg_server_chain_link.go b/x/profiles/keeper/msg_server_chain_link.go index 23074bfae3..ab8b15cdc9 100644 --- a/x/profiles/keeper/msg_server_chain_link.go +++ b/x/profiles/keeper/msg_server_chain_link.go @@ -25,7 +25,7 @@ func (k msgServer) LinkChainAccount(goCtx context.Context, msg *types.MsgLinkCha ctx.EventManager().EmitEvent(sdk.NewEvent( types.EventTypeLinkChainAccount, - sdk.NewAttribute(types.AttributeChainLinkSourceAddress, srcAddrData.GetAddress()), + sdk.NewAttribute(types.AttributeChainLinkSourceAddress, srcAddrData.GetValue()), sdk.NewAttribute(types.AttributeChainLinkSourceChainName, msg.ChainConfig.Name), sdk.NewAttribute(types.AttributeChainLinkDestinationAddress, msg.Signer), sdk.NewAttribute(types.AttributeChainLinkCreationTime, link.CreationTime.Format(time.RFC3339Nano)), diff --git a/x/profiles/keeper/msg_server_chain_link_test.go b/x/profiles/keeper/msg_server_chain_link_test.go index 3a88586770..458152fcc6 100644 --- a/x/profiles/keeper/msg_server_chain_link_test.go +++ b/x/profiles/keeper/msg_server_chain_link_test.go @@ -139,7 +139,7 @@ func (suite *KeeperTestSuite) Test_handleMsgLinkChainAccount() { suite.Require().Equal(test.expEvents, suite.ctx.EventManager().Events()) addrData := test.msg.ChainAddress.GetCachedValue().(types.AddressData) - _, found := suite.k.GetChainLink(suite.ctx, destAddr, test.msg.ChainConfig.Name, addrData.GetAddress()) + _, found := suite.k.GetChainLink(suite.ctx, destAddr, test.msg.ChainConfig.Name, addrData.GetValue()) suite.Require().True(found) _, found, err := suite.k.GetProfile(suite.ctx, destAddr) diff --git a/x/profiles/keeper/msg_server_dtag_transfers.go b/x/profiles/keeper/msg_server_dtag_transfers.go index 2640178567..21286fde50 100644 --- a/x/profiles/keeper/msg_server_dtag_transfers.go +++ b/x/profiles/keeper/msg_server_dtag_transfers.go @@ -24,7 +24,7 @@ func (k msgServer) RequestDTagTransfer(goCtx context.Context, msg *types.MsgRequ } if !found { - return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "the request recipient does not have a profile yet") + return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "the request recipient does not have a profile") } dTagToTrade := profile.DTag @@ -148,8 +148,8 @@ func (k msgServer) AcceptDTagTransfer(goCtx context.Context, msg *types.MsgAccep return nil, err } - k.DeleteAllDTagTransferRequests(ctx, msg.Receiver) - k.DeleteAllDTagTransferRequests(ctx, msg.Sender) + k.DeleteAllUserIncomingDTagTransferRequests(ctx, msg.Receiver) + k.DeleteAllUserIncomingDTagTransferRequests(ctx, msg.Sender) ctx.EventManager().EmitEvent(sdk.NewEvent( types.EventTypeDTagTransferAccept, diff --git a/x/profiles/keeper/msg_server_dtag_transfers_test.go b/x/profiles/keeper/msg_server_dtag_transfers_test.go index f3c90ba825..16ac788bd2 100644 --- a/x/profiles/keeper/msg_server_dtag_transfers_test.go +++ b/x/profiles/keeper/msg_server_dtag_transfers_test.go @@ -1,7 +1,8 @@ package keeper_test import ( - codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -10,15 +11,8 @@ import ( ) func (suite *KeeperTestSuite) Test_handleMsgRequestDTagTransfer() { - otherAddr, err := sdk.AccAddressFromBech32(suite.testData.otherUser) - suite.Require().NoError(err) - - otherAccAny, err := codectypes.NewAnyWithValue(authtypes.NewBaseAccountWithAddress(otherAddr)) - suite.Require().NoError(err) - tests := []struct { name string - storedProfiles []*types.Profile storedDTagReqs []types.DTagTransferRequest storedBlocks []types.UserBlock msg *types.MsgRequestDTagTransfer @@ -41,57 +35,52 @@ func (suite *KeeperTestSuite) Test_handleMsgRequestDTagTransfer() { }, { name: "No DTag to transfer returns error", - msg: types.NewMsgRequestDTagTransfer(suite.testData.user, suite.testData.otherUser), + msg: types.NewMsgRequestDTagTransfer(suite.testData.otherUser, "user"), expEvents: sdk.EmptyEvents(), shouldErr: true, }, { name: "Already present request returns error", - storedProfiles: []*types.Profile{ - suite.testData.profile.Profile, - { - DTag: "test-dtag", - Account: otherAccAny, - }, - }, storedDTagReqs: []types.DTagTransferRequest{ - types.NewDTagTransferRequest("dtag", suite.testData.profile.GetAddress().String(), suite.testData.otherUser), + types.NewDTagTransferRequest("dtag", suite.testData.user, suite.testData.otherUser), }, - msg: types.NewMsgRequestDTagTransfer(suite.testData.profile.GetAddress().String(), suite.testData.otherUser), + msg: types.NewMsgRequestDTagTransfer(suite.testData.user, suite.testData.otherUser), expEvents: sdk.EmptyEvents(), shouldErr: true, }, { - name: "Not already present request saved correctly", - storedProfiles: []*types.Profile{ - suite.testData.profile.Profile, - }, - msg: types.NewMsgRequestDTagTransfer(suite.testData.user, suite.testData.profile.GetAddress().String()), + name: "Not already present request saved correctly", + msg: types.NewMsgRequestDTagTransfer(suite.testData.user, suite.testData.otherUser), shouldErr: false, expEvents: sdk.Events{ sdk.NewEvent( types.EventTypeDTagTransferRequest, - sdk.NewAttribute(types.AttributeDTagToTrade, "dtag"), + sdk.NewAttribute(types.AttributeDTagToTrade, fmt.Sprintf("%s-dtag", suite.testData.otherUser)), sdk.NewAttribute(types.AttributeRequestSender, suite.testData.user), - sdk.NewAttribute(types.AttributeRequestReceiver, suite.testData.profile.GetAddress().String()), + sdk.NewAttribute(types.AttributeRequestReceiver, suite.testData.otherUser), ), }, }, } for _, test := range tests { - suite.SetupTest() suite.Run(test.name, func() { + suite.SetupTest() + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + for _, req := range test.storedDTagReqs { err := suite.k.SaveDTagTransferRequest(suite.ctx, req) suite.Require().NoError(err) } - for _, profile := range test.storedProfiles { - err := suite.k.StoreProfile(suite.ctx, profile) - suite.Require().NoError(err) - } - for _, block := range test.storedBlocks { err := suite.k.SaveUserBlock(suite.ctx, block) suite.Require().NoError(err) @@ -222,15 +211,25 @@ func (suite *KeeperTestSuite) Test_handleMsgAcceptDTagTransfer() { for _, test := range tests { suite.SetupTest() suite.Run(test.name, func() { + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err = suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + suite.k.SetParams(suite.ctx, types.DefaultParams()) - for _, req := range test.storedDTagReqs { - err := suite.k.SaveDTagTransferRequest(suite.ctx, req) + for _, prof := range test.storedProfiles { + err := suite.k.StoreProfile(suite.ctx, prof) suite.Require().NoError(err) } - for _, prof := range test.storedProfiles { - err := suite.k.StoreProfile(suite.ctx, prof) + for _, req := range test.storedDTagReqs { + err := suite.k.SaveDTagTransferRequest(suite.ctx, req) suite.Require().NoError(err) } @@ -282,13 +281,23 @@ func (suite *KeeperTestSuite) Test_handleMsgRefuseDTagRequest() { for _, test := range tests { suite.SetupTest() suite.Run(test.name, func() { + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + for _, req := range test.storedDTagReqs { err := suite.k.SaveDTagTransferRequest(suite.ctx, req) suite.Require().NoError(err) } server := keeper.NewMsgServerImpl(suite.k) - _, err := server.RefuseDTagTransfer(sdk.WrapSDKContext(suite.ctx), test.msg) + _, err = server.RefuseDTagTransfer(sdk.WrapSDKContext(suite.ctx), test.msg) if test.shouldErr { suite.Require().Error(err) @@ -335,13 +344,23 @@ func (suite *KeeperTestSuite) Test_handleMsgCancelDTagRequest() { for _, test := range tests { suite.SetupTest() suite.Run(test.name, func() { + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + for _, req := range test.storedDTagReqs { err := suite.k.SaveDTagTransferRequest(suite.ctx, req) suite.Require().NoError(err) } server := keeper.NewMsgServerImpl(suite.k) - _, err := server.CancelDTagTransfer(sdk.WrapSDKContext(suite.ctx), test.msg) + _, err = server.CancelDTagTransfer(sdk.WrapSDKContext(suite.ctx), test.msg) if test.shouldErr { suite.Require().Error(err) diff --git a/x/profiles/keeper/msg_server_relationships_test.go b/x/profiles/keeper/msg_server_relationships_test.go index da9378d8a7..5f3ece418a 100644 --- a/x/profiles/keeper/msg_server_relationships_test.go +++ b/x/profiles/keeper/msg_server_relationships_test.go @@ -21,15 +21,15 @@ func (suite *KeeperTestSuite) Test_handleMsgCreateRelationship() { name: "Relationship sender blocked by receiver returns error", storedBlock: []types.UserBlock{ types.NewUserBlock( - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + suite.testData.otherUser, + suite.testData.user, "test", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), }, msg: types.NewMsgCreateRelationship( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.user, + suite.testData.otherUser, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), expErr: true, @@ -38,14 +38,14 @@ func (suite *KeeperTestSuite) Test_handleMsgCreateRelationship() { name: "Existing relationship returns error", storedRelationships: []types.Relationship{ types.NewRelationship( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.user, + suite.testData.otherUser, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), }, msg: types.NewMsgCreateRelationship( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.user, + suite.testData.otherUser, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), expErr: true, @@ -53,23 +53,23 @@ func (suite *KeeperTestSuite) Test_handleMsgCreateRelationship() { { name: "Relationship has been saved correctly", msg: types.NewMsgCreateRelationship( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.user, + suite.testData.otherUser, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), expErr: false, expEvents: sdk.Events{ sdk.NewEvent( types.EventTypeRelationshipCreated, - sdk.NewAttribute(types.AttributeRelationshipSender, "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"), - sdk.NewAttribute(types.AttributeRelationshipReceiver, "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns"), + sdk.NewAttribute(types.AttributeRelationshipSender, suite.testData.user), + sdk.NewAttribute(types.AttributeRelationshipReceiver, suite.testData.otherUser), sdk.NewAttribute(types.AttributeRelationshipSubspace, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e"), ), }, expRelationships: []types.Relationship{ types.NewRelationship( - "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.user, + suite.testData.otherUser, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ), }, @@ -79,6 +79,16 @@ func (suite *KeeperTestSuite) Test_handleMsgCreateRelationship() { for _, test := range tests { suite.SetupTest() suite.Run(test.name, func() { + + profile := suite.CreateProfileFromAddress(suite.testData.user) + otherProfile := suite.CreateProfileFromAddress(suite.testData.otherUser) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) + + err = suite.k.StoreProfile(suite.ctx, otherProfile) + suite.Require().NoError(err) + for _, rel := range test.storedRelationships { err := suite.k.SaveRelationship(suite.ctx, rel) suite.Require().NoError(err) @@ -90,7 +100,7 @@ func (suite *KeeperTestSuite) Test_handleMsgCreateRelationship() { } handler := keeper.NewMsgServerImpl(suite.k) - _, err := handler.CreateRelationship(sdk.WrapSDKContext(suite.ctx), test.msg) + _, err = handler.CreateRelationship(sdk.WrapSDKContext(suite.ctx), test.msg) if test.expErr { suite.Require().Error(err) @@ -117,22 +127,22 @@ func (suite *KeeperTestSuite) Test_handleMsgDeleteRelationship() { { name: "Relationship not found returns error", stored: []types.Relationship{ - types.NewRelationship("creator", "recipient", "subspace"), + types.NewRelationship(suite.testData.user, "recipient", "subspace"), }, - msg: types.NewMsgDeleteRelationship("creator", "recipient", "other_subspace"), + msg: types.NewMsgDeleteRelationship(suite.testData.user, "recipient", "other_subspace"), expErr: true, }, { name: "Existing relationship is removed properly and leaves empty array", stored: []types.Relationship{ - types.NewRelationship("creator", "recipient", "subspace"), + types.NewRelationship(suite.testData.user, "recipient", "subspace"), }, - msg: types.NewMsgDeleteRelationship("creator", "recipient", "subspace"), + msg: types.NewMsgDeleteRelationship(suite.testData.user, "recipient", "subspace"), expErr: false, expEvents: sdk.Events{ sdk.NewEvent( types.EventTypeRelationshipsDeleted, - sdk.NewAttribute(types.AttributeRelationshipSender, "creator"), + sdk.NewAttribute(types.AttributeRelationshipSender, suite.testData.user), sdk.NewAttribute(types.AttributeRelationshipReceiver, "recipient"), sdk.NewAttribute(types.AttributeRelationshipSubspace, "subspace"), ), @@ -141,29 +151,34 @@ func (suite *KeeperTestSuite) Test_handleMsgDeleteRelationship() { { name: "Existing relationship is removed properly and leaves not empty array", stored: []types.Relationship{ - types.NewRelationship("creator", "recipient", "subspace"), - types.NewRelationship("creator", "recipient", "other_subspace"), + types.NewRelationship(suite.testData.user, "recipient", "subspace"), + types.NewRelationship(suite.testData.user, "recipient", "other_subspace"), }, - msg: types.NewMsgDeleteRelationship("creator", "recipient", "subspace"), + msg: types.NewMsgDeleteRelationship(suite.testData.user, "recipient", "subspace"), expErr: false, expEvents: sdk.Events{ sdk.NewEvent( types.EventTypeRelationshipsDeleted, - sdk.NewAttribute(types.AttributeRelationshipSender, "creator"), + sdk.NewAttribute(types.AttributeRelationshipSender, suite.testData.user), sdk.NewAttribute(types.AttributeRelationshipReceiver, "recipient"), sdk.NewAttribute(types.AttributeRelationshipSubspace, "subspace"), ), }, expRelationships: []types.Relationship{ - types.NewRelationship("creator", "recipient", "other_subspace"), + types.NewRelationship(suite.testData.user, "recipient", "other_subspace"), }, }, } for _, test := range tests { test := test + suite.SetupTest() suite.Run(test.name, func() { - suite.SetupTest() + + profile := suite.CreateProfileFromAddress(suite.testData.user) + + err := suite.k.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) for _, relationship := range test.stored { err := suite.k.SaveRelationship(suite.ctx, relationship) @@ -171,7 +186,7 @@ func (suite *KeeperTestSuite) Test_handleMsgDeleteRelationship() { } service := keeper.NewMsgServerImpl(suite.k) - _, err := service.DeleteRelationship(sdk.WrapSDKContext(suite.ctx), test.msg) + _, err = service.DeleteRelationship(sdk.WrapSDKContext(suite.ctx), test.msg) if test.expErr { suite.Require().Error(err) diff --git a/x/profiles/keeper/msgs_server_profile.go b/x/profiles/keeper/msgs_server_profile.go index 7e5ad071c6..37d91e82a6 100644 --- a/x/profiles/keeper/msgs_server_profile.go +++ b/x/profiles/keeper/msgs_server_profile.go @@ -43,11 +43,6 @@ func (k msgServer) SaveProfile(goCtx context.Context, msg *types.MsgSaveProfile) } } - // If the DTag changes, delete all the previous DTag transfer requests - if profile.DTag != msg.DTag { - k.DeleteAllDTagTransferRequests(ctx, msg.Creator) - } - // Update the existing profile with the values provided from the user updated, err := profile.Update(types.NewProfileUpdate( msg.DTag, @@ -66,7 +61,8 @@ func (k msgServer) SaveProfile(goCtx context.Context, msg *types.MsgSaveProfile) } // Save the profile - if err := k.StoreProfile(ctx, updated); err != nil { + err = k.StoreProfile(ctx, updated) + if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error()) } diff --git a/x/profiles/keeper/relay_chain_links.go b/x/profiles/keeper/relay_chain_links.go index 0421d13b36..4e9a6f8603 100644 --- a/x/profiles/keeper/relay_chain_links.go +++ b/x/profiles/keeper/relay_chain_links.go @@ -40,7 +40,7 @@ func (k Keeper) OnRecvLinkChainAccountPacket( // Get the destination proof public key var pubKey cryptotypes.PubKey - err = k.cdc.UnpackAny(data.DestinationProof.GetPubKey(), &pubKey) + err = k.cdc.UnpackAny(data.DestinationProof.PubKey, &pubKey) if err != nil { return packetAck, sdkerrors.Wrap(sdkerrors.ErrInvalidType, "invalid public key type") } @@ -53,7 +53,8 @@ func (k Keeper) OnRecvLinkChainAccountPacket( } // Verify the destination proof - err = data.DestinationProof.Verify(k.cdc) + destAddrData := types.NewBech32Address(data.DestinationAddress, sdk.GetConfig().GetBech32AccountAddrPrefix()) + err = data.DestinationProof.Verify(k.cdc, destAddrData) if err != nil { return packetAck, err } @@ -65,6 +66,6 @@ func (k Keeper) OnRecvLinkChainAccountPacket( return packetAck, err } - packetAck.SourceAddress = srcAddrData.GetAddress() + packetAck.SourceAddress = srcAddrData.GetValue() return packetAck, nil } diff --git a/x/profiles/module_ibc.go b/x/profiles/module_ibc.go index cfdbb3105d..bc3d8d73f9 100644 --- a/x/profiles/module_ibc.go +++ b/x/profiles/module_ibc.go @@ -242,7 +242,7 @@ func handleLinkChainAccountPacketData( sdk.NewEvent( types.EventTypeLinkChainAccountPacket, sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), - sdk.NewAttribute(types.AttributeChainLinkSourceAddress, address.GetAddress()), + sdk.NewAttribute(types.AttributeChainLinkSourceAddress, address.GetValue()), sdk.NewAttribute(types.AttributeChainLinkSourceChainName, packetData.SourceChainConfig.Name), sdk.NewAttribute(types.AttributeChainLinkDestinationAddress, packetData.DestinationAddress), sdk.NewAttribute(types.AttributeKeyAckSuccess, fmt.Sprintf("%t", true)), diff --git a/x/profiles/simulation/decoder.go b/x/profiles/simulation/decoder.go index 177d23e8ea..2343911d1a 100644 --- a/x/profiles/simulation/decoder.go +++ b/x/profiles/simulation/decoder.go @@ -4,9 +4,8 @@ import ( "bytes" "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/kv" "github.com/desmos-labs/desmos/x/profiles/types" @@ -18,8 +17,8 @@ func NewDecodeStore(cdc codec.Marshaler) func(kvA, kvB kv.Pair) string { return func(kvA, kvB kv.Pair) string { switch { case bytes.HasPrefix(kvA.Key, types.DTagPrefix): - addressA := sdk.AccAddress(bytes.TrimPrefix(kvA.Value, types.DTagPrefix)).String() - addressB := sdk.AccAddress(bytes.TrimPrefix(kvB.Value, types.DTagPrefix)).String() + addressA := sdk.AccAddress(kvA.Value).String() + addressB := sdk.AccAddress(kvB.Value).String() return fmt.Sprintf("DTagAddressA: %s\nDTagAddressB: %s\n", addressA, addressB) case bytes.HasPrefix(kvA.Key, types.DTagTransferRequestPrefix): @@ -36,11 +35,25 @@ func NewDecodeStore(cdc codec.Marshaler) func(kvA, kvB kv.Pair) string { relationshipA, relationshipB) case bytes.HasPrefix(kvA.Key, types.UsersBlocksStorePrefix): - var userBlocksA, userBlocksB types.UserBlocks - cdc.MustUnmarshalBinaryBare(kvA.Value, &userBlocksA) - cdc.MustUnmarshalBinaryBare(kvB.Value, &userBlocksB) - return fmt.Sprintf("User blocks A: %s\nUser blocks B: %s\n", - userBlocksA.Blocks, userBlocksB.Blocks) + var userBlockA, userBlockB types.UserBlock + cdc.MustUnmarshalBinaryBare(kvA.Value, &userBlockA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &userBlockB) + return fmt.Sprintf("User block A: %s\nUser block B: %s\n", + userBlockA, userBlockB) + + case bytes.HasPrefix(kvA.Key, types.ChainLinksPrefix): + var chainLinkA, chainLinkB types.ChainLink + cdc.MustUnmarshalBinaryBare(kvA.Value, &chainLinkA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &chainLinkB) + return fmt.Sprintf("Chain link A: %s\nChain link B: %s\n", + chainLinkA.String(), chainLinkB.String()) + + case bytes.HasPrefix(kvA.Key, types.UserApplicationLinkPrefix): + var applicationLinkA, applicationLinkB types.ApplicationLink + cdc.MustUnmarshalBinaryBare(kvA.Value, &applicationLinkA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &applicationLinkB) + return fmt.Sprintf("Application link A: %s\nApplication link B: %s\n", + applicationLinkA.String(), applicationLinkB.String()) default: panic(fmt.Sprintf("unexpected %s key %X (%s)", types.ModuleName, kvA.Key, kvA.Key)) diff --git a/x/profiles/simulation/decoder_test.go b/x/profiles/simulation/decoder_test.go index 67b1680944..5d16daeb57 100644 --- a/x/profiles/simulation/decoder_test.go +++ b/x/profiles/simulation/decoder_test.go @@ -12,8 +12,6 @@ import ( "github.com/desmos-labs/desmos/app" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/desmos-labs/desmos/x/profiles/simulation" "github.com/desmos-labs/desmos/x/profiles/types" ) @@ -22,43 +20,27 @@ func TestDecodeStore(t *testing.T) { cdc, _ := app.MakeCodecs() dec := simulation.NewDecodeStore(cdc) + addr, err := sdk.AccAddressFromBech32("cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns") + require.NoError(t, err) + request := types.NewDTagTransferRequest( "dtag", "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", ) - addr, err := sdk.AccAddressFromBech32("cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns") - require.NoError(t, err) - - firstAddr := ed25519.GenPrivKey().PubKey().Address().String() - secondAddr := ed25519.GenPrivKey().PubKey().Address().String() - relationship := types.NewRelationship( - firstAddr, - secondAddr, + "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ) - relBz, err := cdc.MarshalBinaryBare(&relationship) - require.NoError(t, err) - - usersBlocks := []types.UserBlock{ - types.NewUserBlock( - firstAddr, - secondAddr, - "reason", - "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", - ), - types.NewUserBlock( - secondAddr, - firstAddr, - "reason", - "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", - ), - } - blocksBz, err := cdc.MarshalBinaryBare(&types.UserBlocks{Blocks: usersBlocks}) - require.NoError(t, err) + userBlock := types.NewUserBlock( + "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + "reason", + "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", + ) kvPairs := kv.Pairs{Pairs: []kv.Pair{ { @@ -73,12 +55,20 @@ func TestDecodeStore(t *testing.T) { Value: cdc.MustMarshalBinaryBare(&request), }, { - Key: types.RelationshipsStoreKey(firstAddr, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", secondAddr), - Value: relBz, + Key: types.RelationshipsStoreKey( + "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", + "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + ), + Value: cdc.MustMarshalBinaryBare(&relationship), }, { - Key: types.UsersBlocksStoreKey(firstAddr, "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", secondAddr), - Value: blocksBz, + Key: types.UserBlockStoreKey( + "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", + "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + ), + Value: cdc.MustMarshalBinaryBare(&userBlock), }, }} @@ -87,9 +77,9 @@ func TestDecodeStore(t *testing.T) { expectedLog string }{ {"DTags", fmt.Sprintf("DTagAddressA: %s\nDTagAddressB: %s\n", "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns")}, - {"Requests", fmt.Sprintf("RequestA: %s\nRequestB: %s\n", request, request)}, - {"Relationships", fmt.Sprintf("Relationships A: %s\nRelationships B: %s\n", relationship, relationship)}, - {"UsersBlocks", fmt.Sprintf("User blocks A: %s\nUser blocks B: %s\n", usersBlocks, usersBlocks)}, + {"DTag transfer request", fmt.Sprintf("RequestA: %s\nRequestB: %s\n", request, request)}, + {"Relationship", fmt.Sprintf("Relationships A: %s\nRelationships B: %s\n", relationship, relationship)}, + {"User block", fmt.Sprintf("User block A: %s\nUser block B: %s\n", userBlock, userBlock)}, {"other", ""}, } diff --git a/x/profiles/simulation/genesis.go b/x/profiles/simulation/genesis.go index 55df035b0b..9fbb5f6f2a 100644 --- a/x/profiles/simulation/genesis.go +++ b/x/profiles/simulation/genesis.go @@ -5,7 +5,8 @@ package simulation import ( "fmt" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/types/module" @@ -14,11 +15,33 @@ import ( // RandomizedGenState generates a random GenesisState for profile func RandomizedGenState(simsState *module.SimulationState) { + profilesNumber := simsState.Rand.Intn(len(simsState.Accounts) - 10) + profiles := NewRandomProfiles(simsState.Rand, simsState.Accounts, profilesNumber) + // Update the auth state with the profiles + var authState authtypes.GenesisState + err := simsState.Cdc.UnmarshalJSON(simsState.GenState[authtypes.ModuleName], &authState) + if err != nil { + panic(err) + } + + genAccounts, err := mergeAccountsWithProfiles(authState.Accounts, profiles) + if err != nil { + panic(err) + } + authState.Accounts = genAccounts + + bz, err := simsState.Cdc.MarshalJSON(&authState) + if err != nil { + panic(err) + } + simsState.GenState[authtypes.ModuleName] = bz + + // Create and set profiles state profileGenesis := types.NewGenesisState( - randomDTagTransferRequests(simsState), - randomRelationships(simsState), - randomUsersBlocks(simsState), + randomDTagTransferRequests(profiles, simsState, simsState.Rand.Intn(profilesNumber/2)), + randomRelationships(profiles, simsState, simsState.Rand.Intn(profilesNumber/2)), + randomUsersBlocks(profiles, simsState, simsState.Rand.Intn(profilesNumber/2)), types.NewParams( RandomNicknameParams(simsState.Rand), RandomDTagParams(simsState.Rand), @@ -29,7 +52,7 @@ func RandomizedGenState(simsState *module.SimulationState) { nil, ) - bz, err := simsState.Cdc.MarshalJSON(profileGenesis) + bz, err = simsState.Cdc.MarshalJSON(profileGenesis) if err != nil { panic(err) } @@ -38,43 +61,103 @@ func RandomizedGenState(simsState *module.SimulationState) { simsState.GenState[types.ModuleName] = simsState.Cdc.MustMarshalJSON(profileGenesis) } +// mergeAccountsWithProfiles merges the provided x/auth genesis accounts with the given profiles, replacing +// any existing account with the associated profile (if existing). +func mergeAccountsWithProfiles(genAccounts []*codectypes.Any, profiles []*types.Profile) ([]*codectypes.Any, error) { + // Unpack the accounts + accounts, err := authtypes.UnpackAccounts(genAccounts) + if err != nil { + return nil, err + } + + for index, account := range accounts { + // See if the account also has a profile + var profile *types.Profile + for _, p := range profiles { + if p.GetAddress().Equals(account.GetAddress()) { + profile = p + break + } + } + + // If found replace the account with the profile + if profile != nil { + accounts[index] = profile + } + } + + return authtypes.PackAccounts(accounts) +} + +// ------------------------------------------------------------------------------------------------------------------- + // randomDTagTransferRequests returns randomly generated genesis dTag transfer requests -func randomDTagTransferRequests(simState *module.SimulationState) []types.DTagTransferRequest { - dTagTransferRequestsNumber := simState.Rand.Intn(20) - - dTagTransferRequests := make([]types.DTagTransferRequest, dTagTransferRequestsNumber) - for i := 0; i < dTagTransferRequestsNumber; i++ { - simAccount, _ := simtypes.RandomAcc(simState.Rand, simState.Accounts) - simAccount2, _ := simtypes.RandomAcc(simState.Rand, simState.Accounts) - dTagTransferRequests[i] = types.NewDTagTransferRequest( - RandomDTag(simState.Rand), - simAccount.Address.String(), - simAccount2.Address.String(), +func randomDTagTransferRequests( + profiles []*types.Profile, simState *module.SimulationState, number int, +) []types.DTagTransferRequest { + + dTagTransferRequests := make([]types.DTagTransferRequest, number) + for i := 0; i < number; { + profile1 := RandomProfile(simState.Rand, profiles) + profile2 := RandomProfile(simState.Rand, profiles) + + // Skip same profiles + if profile1.GetAddress().Equals(profile2.GetAddress()) { + continue + } + + request := types.NewDTagTransferRequest( + profile2.DTag, + profile1.GetAddress().String(), + profile2.GetAddress().String(), ) + + // Skip duplicated requests + if !containsDTagTransferRequest(dTagTransferRequests, request) { + dTagTransferRequests[i] = request + i++ + } } return dTagTransferRequests } +func containsDTagTransferRequest(slice []types.DTagTransferRequest, request types.DTagTransferRequest) bool { + for _, req := range slice { + if req.Sender == request.Sender && req.Receiver == request.Receiver { + return true + } + } + return false +} + +// ------------------------------------------------------------------------------------------------------------------- + // randomRelationships returns randomly generated genesis relationships and their associated users - IDs map -func randomRelationships(simState *module.SimulationState) []types.Relationship { - relationshipsNumber := simState.Rand.Intn(simtypes.RandIntBetween(simState.Rand, 1, 30)) - - relationships := make([]types.Relationship, relationshipsNumber) - for index := 0; index < relationshipsNumber; { - sender, _ := simtypes.RandomAcc(simState.Rand, simState.Accounts) - receiver, _ := simtypes.RandomAcc(simState.Rand, simState.Accounts) - if !sender.Equals(receiver) { - newRelationship := types.NewRelationship( - sender.Address.String(), - receiver.Address.String(), - RandomSubspace(simState.Rand), - ) - if !containsRelationship(relationships, newRelationship) { - relationships[index] = newRelationship - index++ - } +func randomRelationships( + profiles []*types.Profile, simState *module.SimulationState, number int, +) []types.Relationship { + relationships := make([]types.Relationship, number) + for index := 0; index < number; { + profile1 := RandomProfile(simState.Rand, profiles) + profile2 := RandomProfile(simState.Rand, profiles) + + // Skip same profiles + if profile1.GetAddress().Equals(profile2.GetAddress()) { + continue + } + + relationship := types.NewRelationship( + profile1.GetAddress().String(), + profile2.GetAddress().String(), + RandomSubspace(simState.Rand), + ) + + if !containsRelationship(relationships, relationship) { + relationships[index] = relationship + index++ } + } return relationships @@ -90,23 +173,44 @@ func containsRelationship(slice []types.Relationship, relationship types.Relatio return false } +// ------------------------------------------------------------------------------------------------------------------- + // randomUsersBlocks -func randomUsersBlocks(simState *module.SimulationState) []types.UserBlock { - usersBlocksNumber := simState.Rand.Intn(simtypes.RandIntBetween(simState.Rand, 1, 30)) - - usersBlocks := make([]types.UserBlock, usersBlocksNumber) - for index := 0; index < usersBlocksNumber; index++ { - blocker, _ := simtypes.RandomAcc(simState.Rand, simState.Accounts) - blocked, _ := simtypes.RandomAcc(simState.Rand, simState.Accounts) - if !blocker.Equals(blocked) { - usersBlocks[index] = types.NewUserBlock( - blocker.Address.String(), - blocked.Address.String(), - "reason", - RandomSubspace(simState.Rand), - ) +func randomUsersBlocks( + profiles []*types.Profile, simState *module.SimulationState, number int, +) []types.UserBlock { + usersBlocks := make([]types.UserBlock, number) + for index := 0; index < number; { + profile1 := RandomProfile(simState.Rand, profiles) + profile2 := RandomProfile(simState.Rand, profiles) + + // Skip same profiles + if profile1.GetAddress().Equals(profile2.GetAddress()) { + continue + } + + block := types.NewUserBlock( + profile1.GetAddress().String(), + profile2.GetAddress().String(), + "reason", + RandomSubspace(simState.Rand), + ) + + if !containsUserBlock(usersBlocks, block) { + usersBlocks[index] = block + index++ } } return usersBlocks } + +// containsUserBlock returns true iff the given slice contains the given block +func containsUserBlock(slice []types.UserBlock, block types.UserBlock) bool { + for _, b := range slice { + if b.Equal(block) { + return true + } + } + return false +} diff --git a/x/profiles/simulation/operations_dtag_transfers.go b/x/profiles/simulation/operations_dtag_transfers.go index 32c1875a15..50caa2a3dd 100644 --- a/x/profiles/simulation/operations_dtag_transfers.go +++ b/x/profiles/simulation/operations_dtag_transfers.go @@ -29,12 +29,12 @@ func SimulateMsgRequestDTagTransfer( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, ) (OperationMsg simtypes.OperationMsg, futureOps []simtypes.FutureOperation, err error) { - sender, request, skip := randomDTagRequestTransferFields(r, ctx, accs, k) + sender, receiver, skip := randomDTagRequestTransferFields(r, ctx, accs, k) if skip { return simtypes.NoOpMsg(types.RouterKey, types.ModuleName, ""), nil, nil } - msg := types.NewMsgRequestDTagTransfer(request.Sender, request.Receiver) + msg := types.NewMsgRequestDTagTransfer(sender.Address.String(), receiver.GetAddress().String()) err = sendMsgRequestDTagTransfer(r, app, ak, bk, msg, ctx, chainID, []cryptotypes.PrivKey{sender.PrivKey}) if err != nil { @@ -85,41 +85,38 @@ func sendMsgRequestDTagTransfer( // randomDTagRequestTransferFields returns random dTagRequest data func randomDTagRequestTransferFields( r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, k keeper.Keeper, -) (simtypes.Account, types.DTagTransferRequest, bool) { +) (sender simtypes.Account, receiver *types.Profile, skip bool) { if len(accs) == 0 { - return simtypes.Account{}, types.DTagTransferRequest{}, true + return simtypes.Account{}, nil, true } // Get random sender account - sender, _ := simtypes.RandomAcc(r, accs) + sender, _ = simtypes.RandomAcc(r, accs) profiles := k.GetProfiles(ctx) if profiles == nil { - return simtypes.Account{}, types.DTagTransferRequest{}, true + return simtypes.Account{}, nil, true } // Get a random Profile receiverProfile := RandomProfile(r, profiles) receiverAddress := receiverProfile.GetAddress() if receiverAddress.Equals(sender.Address) { - return simtypes.Account{}, types.DTagTransferRequest{}, true + return simtypes.Account{}, nil, true } // Skip if the sender is blocked if k.IsUserBlocked(ctx, receiverAddress.String(), sender.Address.String()) { - return simtypes.Account{}, types.DTagTransferRequest{}, true + return simtypes.Account{}, nil, true } - // Create a request - req := types.NewDTagTransferRequest(receiverProfile.DTag, sender.Address.String(), receiverAddress.String()) - // Skip if requests already exists _, found, err := k.GetDTagTransferRequest(ctx, sender.Address.String(), receiverAddress.String()) if err != nil || found { - return simtypes.Account{}, types.DTagTransferRequest{}, true + return simtypes.Account{}, nil, true } - return sender, req, false + return sender, receiverProfile, false } // ___________________________________________________________________________________________________________________ @@ -132,17 +129,12 @@ func SimulateMsgAcceptDTagTransfer( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, ) (OperationMsg simtypes.OperationMsg, futureOps []simtypes.FutureOperation, err error) { - acc, request, dTag, skip := randomDTagAcceptRequestTransferFields(r, ctx, accs, k, ak) + acc, request, dTag, skip := randomDTagAcceptRequestTransferFields(r, ctx, accs, k) if skip { return simtypes.NoOpMsg(types.RouterKey, types.ModuleName, ""), nil, nil } - msg := types.NewMsgAcceptDTagTransfer( - dTag, - request.Sender, - request.Receiver, - ) - + msg := types.NewMsgAcceptDTagTransfer(dTag, request.Sender, request.Receiver) err = sendMsgMsgAcceptDTagTransfer(r, app, ak, bk, msg, ctx, chainID, []cryptotypes.PrivKey{acc.PrivKey}) if err != nil { return simtypes.NoOpMsg(types.RouterKey, types.ModuleName, "MsgAcceptDTagTransfer"), nil, err @@ -191,7 +183,7 @@ func sendMsgMsgAcceptDTagTransfer( // randomDTagAcceptRequestTransferFields returns random dTagRequest data and a random dTag func randomDTagAcceptRequestTransferFields( - r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, k keeper.Keeper, ak authkeeper.AccountKeeper, + r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, k keeper.Keeper, ) (simtypes.Account, types.DTagTransferRequest, string, bool) { if len(accs) == 0 { return simtypes.Account{}, types.DTagTransferRequest{}, "", true @@ -202,36 +194,16 @@ func randomDTagAcceptRequestTransferFields( receiver, _ := simtypes.RandomAcc(r, accs) // skip if the two addresses are equals - if sender.Equals(receiver) { + if sender.Address.Equals(receiver.Address) { return simtypes.Account{}, types.DTagTransferRequest{}, "", true } - req := types.NewDTagTransferRequest( - "dtag", - receiver.Address.String(), - sender.Address.String(), - ) - // Skip if requests doesn't exists - _, found, err := k.GetDTagTransferRequest(ctx, sender.Address.String(), receiver.Address.String()) + req, found, err := k.GetDTagTransferRequest(ctx, sender.Address.String(), receiver.Address.String()) if err != nil || !found { return simtypes.Account{}, types.DTagTransferRequest{}, "", true } - profile := NewRandomProfile(r, ak.GetAccount(ctx, sender.Address)) - profile.DTag = "dtag" - - err = k.ValidateProfile(ctx, profile) - if err != nil { - return simtypes.Account{}, types.DTagTransferRequest{}, "", true - } - - err = k.StoreProfile(ctx, profile) - - if err != nil { - return simtypes.Account{}, types.DTagTransferRequest{}, "", true - } - return receiver, req, RandomDTag(r), false } @@ -251,11 +223,7 @@ func SimulateMsgRefuseDTagTransfer( return simtypes.NoOpMsg(types.RouterKey, types.ModuleName, ""), nil, nil } - msg := types.NewMsgRefuseDTagTransferRequest( - sender.Address.String(), - receiver.Address.String(), - ) - + msg := types.NewMsgRefuseDTagTransferRequest(sender.Address.String(), receiver.Address.String()) err = sendMsgMsgRefuseDTagTransfer(r, app, ak, bk, msg, ctx, chainID, []cryptotypes.PrivKey{receiver.PrivKey}) if err != nil { return simtypes.NoOpMsg(types.RouterKey, types.ModuleName, "MsgRefuseDTagTransfer"), nil, err diff --git a/x/profiles/simulation/operations_relationships.go b/x/profiles/simulation/operations_relationships.go index 66be489e5f..f7bb225a6c 100644 --- a/x/profiles/simulation/operations_relationships.go +++ b/x/profiles/simulation/operations_relationships.go @@ -90,14 +90,24 @@ func randomRelationshipFields( // Get random accounts sender, _ := simtypes.RandomAcc(r, accs) receiver, _ := simtypes.RandomAcc(r, accs) - subspace := RandomSubspace(r) - // skip if the two relationship are equals + // Skip if the send and receiver are equals if sender.Equals(receiver) { return simtypes.Account{}, types.Relationship{}, true } + // Skip if the creator does not have a profile + if !k.HasProfile(ctx, sender.Address.String()) { + return simtypes.Account{}, types.Relationship{}, true + } + + // Skip if the receiver does not have a profile + if !k.HasProfile(ctx, receiver.Address.String()) { + return simtypes.Account{}, types.Relationship{}, true + } + + // Skip if the receiver has block the sender if k.HasUserBlocked(ctx, receiver.Address.String(), sender.Address.String(), subspace) { return simtypes.Account{}, types.Relationship{}, true } diff --git a/x/profiles/simulation/operations_user_blocks.go b/x/profiles/simulation/operations_user_blocks.go index 69b0df7e4d..1c6607e889 100644 --- a/x/profiles/simulation/operations_user_blocks.go +++ b/x/profiles/simulation/operations_user_blocks.go @@ -95,11 +95,16 @@ func randomUserBlocksFields( blocker, _ := simtypes.RandomAcc(r, accs) blocked, _ := simtypes.RandomAcc(r, accs) - // skip if the two address are equals + // Skip if the blocker and blocked user are equals if blocker.Equals(blocked) { return simtypes.Account{}, nil, true } + // Skip if the blocker does not have a profile + if !k.HasProfile(ctx, blocker.Address.String()) { + return simtypes.Account{}, nil, true + } + // skip if user block already exists userBlocks := k.GetUserBlocks(ctx, blocker.Address.String()) for _, userBlock := range userBlocks { diff --git a/x/profiles/simulation/utils.go b/x/profiles/simulation/utils.go index 641b95c276..e9cf24be7c 100644 --- a/x/profiles/simulation/utils.go +++ b/x/profiles/simulation/utils.go @@ -55,6 +55,21 @@ var ( } ) +// NewRandomProfiles returns number random profiles +func NewRandomProfiles(r *rand.Rand, accounts []simtypes.Account, number int) []*types.Profile { + var profiles = make([]*types.Profile, number) + for index := range profiles { + account := accounts[index] + profiles[index] = NewRandomProfile(r, authtypes.NewBaseAccount( + account.Address, + account.PubKey, + 0, + 0, + )) + } + return profiles +} + // NewRandomProfile return a random ProfileData from random data and the given account // nolint:interfacer func NewRandomProfile(r *rand.Rand, account authtypes.AccountI) *types.Profile { @@ -87,7 +102,7 @@ func RandomDTagTransferRequest(r *rand.Rand, requests []types.DTagTransferReques // RandomDTag return a random DTag func RandomDTag(r *rand.Rand) string { // DTag must be at least 3 characters and at most 30 - return simtypes.RandStringOfLength(r, simtypes.RandIntBetween(r, 3, 30)) + return simtypes.RandStringOfLength(r, simtypes.RandIntBetween(r, 6, 30)) } // RandomNickname return a random nickname @@ -159,3 +174,8 @@ func RandomUserBlock(r *rand.Rand, userBlocks []types.UserBlock) types.UserBlock idx := r.Intn(len(userBlocks)) return userBlocks[idx] } + +// RandomDTagTransferRequests returns a new random DTag transfer request from the ones given +func RandomDTagTransferRequests(r *rand.Rand, requests []types.DTagTransferRequest) types.DTagTransferRequest { + return requests[r.Intn(len(requests))-1] +} diff --git a/x/profiles/types/account.go b/x/profiles/types/account.go index 4bd5afe827..f8af80f3f2 100644 --- a/x/profiles/types/account.go +++ b/x/profiles/types/account.go @@ -191,11 +191,9 @@ type profilePretty struct { Bio string `json:"bio" yaml:"bio"` Pictures Pictures `json:"pictures" yaml:"pictures"` CreationDate time.Time `json:"creation_date" yaml:"creation_date"` - ChainLinks []ChainLink `json:"chain_links" yaml:"chain_links"` } -// Ensure that acc -//// String implements authtypes.AccountIount implements stringer +// String implements authtypes.AccountI implements stringer func (p *Profile) String() string { out, _ := p.MarshalYAML() return out.(string) diff --git a/x/profiles/types/genesis.pb.go b/x/profiles/types/genesis.pb.go index 2eb4231b51..a58a9c6dc2 100644 --- a/x/profiles/types/genesis.pb.go +++ b/x/profiles/types/genesis.pb.go @@ -67,55 +67,6 @@ func (m *GenesisState) XXX_DiscardUnknown() { var xxx_messageInfo_GenesisState proto.InternalMessageInfo -func (m *GenesisState) GetDTagTransferRequests() []DTagTransferRequest { - if m != nil { - return m.DTagTransferRequests - } - return nil -} - -func (m *GenesisState) GetRelationships() []Relationship { - if m != nil { - return m.Relationships - } - return nil -} - -func (m *GenesisState) GetBlocks() []UserBlock { - if m != nil { - return m.Blocks - } - return nil -} - -func (m *GenesisState) GetParams() Params { - if m != nil { - return m.Params - } - return Params{} -} - -func (m *GenesisState) GetIBCPortID() string { - if m != nil { - return m.IBCPortID - } - return "" -} - -func (m *GenesisState) GetChainLinks() []ChainLink { - if m != nil { - return m.ChainLinks - } - return nil -} - -func (m *GenesisState) GetApplicationLinks() []ApplicationLink { - if m != nil { - return m.ApplicationLinks - } - return nil -} - func init() { proto.RegisterType((*GenesisState)(nil), "desmos.profiles.v1beta1.GenesisState") } @@ -125,40 +76,40 @@ func init() { } var fileDescriptor_24f308c1ccf2d582 = []byte{ - // 520 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0xc1, 0x8a, 0xd3, 0x4e, - 0x1c, 0xc7, 0x9b, 0xff, 0xee, 0xf6, 0x4f, 0xa7, 0x2e, 0x68, 0xac, 0x1a, 0x2a, 0x26, 0x25, 0xb2, - 0x50, 0x70, 0x4d, 0xdc, 0x7a, 0x13, 0x3c, 0x98, 0x2e, 0x68, 0x41, 0x64, 0x89, 0x2b, 0x82, 0x1e, - 0xca, 0x24, 0x9d, 0x4d, 0x87, 0x26, 0x99, 0x71, 0x66, 0x2a, 0xee, 0x5b, 0xf8, 0x0a, 0x3e, 0x86, - 0x6f, 0xb0, 0xc7, 0x3d, 0x7a, 0x0a, 0x92, 0xbe, 0x41, 0x9f, 0x40, 0x92, 0x99, 0xba, 0xe9, 0xb2, - 0x21, 0xde, 0xc2, 0xe4, 0xf3, 0xfd, 0x7c, 0x7f, 0x99, 0xcc, 0x80, 0x83, 0x19, 0xe2, 0x09, 0xe1, - 0x2e, 0x65, 0xe4, 0x0c, 0xc7, 0x88, 0xbb, 0x5f, 0x8f, 0x02, 0x24, 0xe0, 0x91, 0x1b, 0xa1, 0x14, - 0x71, 0xcc, 0x1d, 0xca, 0x88, 0x20, 0xfa, 0x03, 0x89, 0x39, 0x1b, 0xcc, 0x51, 0x58, 0xbf, 0x17, - 0x91, 0x88, 0x94, 0x8c, 0x5b, 0x3c, 0x49, 0xbc, 0xff, 0xa4, 0xce, 0x9a, 0x90, 0x19, 0x8a, 0xf9, - 0x94, 0x42, 0x06, 0x13, 0xe5, 0xee, 0x1f, 0x36, 0xc1, 0x72, 0x5d, 0xd1, 0xa3, 0x06, 0x9a, 0xa1, - 0x18, 0x0a, 0x4c, 0x52, 0x3e, 0xc7, 0x94, 0xff, 0x63, 0x66, 0x26, 0x60, 0x34, 0x65, 0xe8, 0xcb, - 0x12, 0x71, 0xb1, 0xc9, 0x3c, 0x6b, 0xc8, 0x84, 0x73, 0x88, 0xd3, 0x69, 0x8c, 0xd3, 0xc5, 0x26, - 0xe1, 0x34, 0x24, 0x20, 0xa5, 0x55, 0xde, 0xfe, 0xb9, 0x07, 0x6e, 0xbd, 0x96, 0xbb, 0xfc, 0x5e, - 0x40, 0x81, 0xf4, 0x1f, 0x1a, 0xb8, 0x5f, 0x8e, 0x22, 0x18, 0x4c, 0xf9, 0x19, 0x62, 0x7f, 0x67, - 0x32, 0xb4, 0xc1, 0xce, 0xb0, 0x3b, 0x3a, 0x74, 0x6a, 0x7e, 0x83, 0x73, 0x7c, 0x0a, 0xa3, 0x53, - 0x95, 0xf2, 0x65, 0xc8, 0x7b, 0x79, 0x91, 0x59, 0xad, 0x3c, 0xb3, 0x7a, 0x37, 0xbc, 0xe4, 0xeb, - 0xcc, 0x7a, 0x74, 0x0e, 0x93, 0xf8, 0x85, 0x7d, 0x73, 0xa3, 0xed, 0xf7, 0x8a, 0x17, 0xd7, 0x63, - 0x3a, 0x01, 0xfb, 0x5b, 0x3b, 0x6c, 0xfc, 0x57, 0x4e, 0x76, 0x50, 0x3b, 0x99, 0x5f, 0xa1, 0x3d, - 0xbb, 0x18, 0x69, 0x9d, 0x59, 0x7d, 0x59, 0xbd, 0xe4, 0x88, 0x5d, 0xfb, 0x63, 0xb6, 0xbf, 0xed, - 0xd7, 0x3f, 0x82, 0x76, 0x10, 0x93, 0x70, 0xc1, 0x8d, 0x9d, 0xb2, 0xc9, 0xae, 0x6d, 0xfa, 0xc0, - 0x11, 0xf3, 0x0a, 0xd4, 0x7b, 0xa8, 0x6a, 0xee, 0x56, 0x6b, 0xa4, 0xc5, 0xf6, 0x95, 0x4e, 0x7f, - 0x07, 0xda, 0xf2, 0x18, 0x1a, 0xbb, 0x03, 0x6d, 0xd8, 0x1d, 0x59, 0xb5, 0xe2, 0x93, 0x12, 0xf3, - 0xee, 0x29, 0xeb, 0xbe, 0xb4, 0xca, 0xb0, 0xed, 0x2b, 0x8b, 0x3e, 0x06, 0x5d, 0x1c, 0x84, 0x53, - 0x4a, 0x98, 0x98, 0xe2, 0x99, 0xb1, 0x37, 0xd0, 0x86, 0x1d, 0xef, 0x71, 0x9e, 0x59, 0x9d, 0x89, - 0x37, 0x3e, 0x21, 0x4c, 0x4c, 0x8e, 0xd7, 0x99, 0xa5, 0xcb, 0x70, 0x85, 0xb4, 0xfd, 0x0e, 0x0e, - 0xc2, 0x12, 0x98, 0xe9, 0x13, 0xd0, 0xad, 0x1c, 0x2c, 0xa3, 0xdd, 0xf0, 0xc9, 0xe3, 0x82, 0x7d, - 0x8b, 0xd3, 0x85, 0xb7, 0x5b, 0x0c, 0xe7, 0x83, 0x70, 0xb3, 0xc0, 0xf5, 0xcf, 0xe0, 0x0e, 0xa4, - 0x34, 0xc6, 0x61, 0xb9, 0x99, 0x4a, 0xf8, 0x7f, 0x29, 0x1c, 0xd6, 0x0a, 0x5f, 0x5d, 0x25, 0x2a, - 0xda, 0xdb, 0x70, 0x7b, 0x99, 0x7b, 0x6f, 0x2e, 0x72, 0x53, 0xbb, 0xcc, 0x4d, 0xed, 0x77, 0x6e, - 0x6a, 0xdf, 0x57, 0x66, 0xeb, 0x72, 0x65, 0xb6, 0x7e, 0xad, 0xcc, 0xd6, 0x27, 0x27, 0xc2, 0x62, - 0xbe, 0x0c, 0x9c, 0x90, 0x24, 0xae, 0x6c, 0x79, 0x1a, 0xc3, 0x80, 0xab, 0x67, 0xf7, 0xdb, 0xd5, - 0xf5, 0x10, 0xe7, 0x14, 0xf1, 0xa0, 0x5d, 0x5e, 0x86, 0xe7, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, - 0x82, 0x83, 0x0a, 0xaf, 0x89, 0x04, 0x00, 0x00, + // 525 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0xc1, 0x6e, 0xd3, 0x30, + 0x1c, 0xc6, 0x1b, 0xb6, 0x15, 0xd5, 0x65, 0x12, 0x84, 0x02, 0x51, 0x11, 0x49, 0x15, 0x34, 0xa9, + 0x12, 0x23, 0x61, 0xe5, 0x36, 0x89, 0x03, 0xe9, 0x24, 0xa8, 0x84, 0xd0, 0x14, 0x86, 0x90, 0xe0, + 0x50, 0x39, 0xa9, 0x97, 0x5a, 0x4d, 0x63, 0x63, 0xbb, 0x88, 0xbd, 0x01, 0x47, 0x1e, 0x01, 0x9e, + 0x84, 0xeb, 0x8e, 0x3b, 0x72, 0x8a, 0x50, 0xfa, 0x06, 0x7d, 0x02, 0x94, 0xd8, 0x65, 0xe9, 0xb4, + 0x28, 0xdc, 0x22, 0xe7, 0xf7, 0xfd, 0xbe, 0x7f, 0x1c, 0x1b, 0xec, 0x4d, 0x10, 0x9f, 0x13, 0xee, + 0x52, 0x46, 0x4e, 0x71, 0x8c, 0xb8, 0xfb, 0xe5, 0x20, 0x40, 0x02, 0x1e, 0xb8, 0x11, 0x4a, 0x10, + 0xc7, 0xdc, 0xa1, 0x8c, 0x08, 0xa2, 0x3f, 0x90, 0x98, 0xb3, 0xc6, 0x1c, 0x85, 0x75, 0x3b, 0x11, + 0x89, 0x48, 0xc1, 0xb8, 0xf9, 0x93, 0xc4, 0xbb, 0x4f, 0xaa, 0xac, 0x73, 0x32, 0x41, 0x31, 0x1f, + 0x53, 0xc8, 0xe0, 0x5c, 0xb9, 0xbb, 0xfb, 0x75, 0xb0, 0x5c, 0x57, 0xf4, 0xa0, 0x86, 0x66, 0x28, + 0x86, 0x02, 0x93, 0x84, 0x4f, 0x31, 0xe5, 0xff, 0x99, 0x99, 0x08, 0x18, 0x8d, 0x19, 0xfa, 0xbc, + 0x40, 0x5c, 0xac, 0x33, 0xcf, 0x6a, 0x32, 0xe1, 0x14, 0xe2, 0x64, 0x1c, 0xe3, 0x64, 0xb6, 0x4e, + 0x38, 0x35, 0x09, 0x48, 0x69, 0x99, 0xb7, 0x7f, 0xed, 0x80, 0x5b, 0xaf, 0xe4, 0x2e, 0xbf, 0x13, + 0x50, 0x20, 0xfd, 0xa7, 0x06, 0xee, 0x17, 0xa3, 0x08, 0x06, 0x13, 0x7e, 0x8a, 0xd8, 0xbf, 0x99, + 0x0c, 0xad, 0xb7, 0xd5, 0x6f, 0x0f, 0xf6, 0x9d, 0x8a, 0xdf, 0xe0, 0x1c, 0x9d, 0xc0, 0xe8, 0x44, + 0xa5, 0x7c, 0x19, 0xf2, 0x5e, 0x9c, 0xa7, 0x56, 0x23, 0x4b, 0xad, 0xce, 0x35, 0x2f, 0xf9, 0x2a, + 0xb5, 0x1e, 0x9d, 0xc1, 0x79, 0x7c, 0x68, 0x5f, 0xdf, 0x68, 0xfb, 0x9d, 0xfc, 0xc5, 0xd5, 0x98, + 0x4e, 0xc0, 0xee, 0xc6, 0x0e, 0x1b, 0x37, 0x8a, 0xc9, 0xf6, 0x2a, 0x27, 0xf3, 0x4b, 0xb4, 0x67, + 0xe7, 0x23, 0xad, 0x52, 0xab, 0x2b, 0xab, 0x17, 0x1c, 0xb1, 0x2b, 0x7f, 0xcc, 0xf6, 0x37, 0xfd, + 0xfa, 0x07, 0xd0, 0x0c, 0x62, 0x12, 0xce, 0xb8, 0xb1, 0x55, 0x34, 0xd9, 0x95, 0x4d, 0xef, 0x39, + 0x62, 0x5e, 0x8e, 0x7a, 0x0f, 0x55, 0xcd, 0xdd, 0x72, 0x8d, 0xb4, 0xd8, 0xbe, 0xd2, 0xe9, 0x6f, + 0x41, 0x53, 0x1e, 0x43, 0x63, 0xbb, 0xa7, 0xf5, 0xdb, 0x03, 0xab, 0x52, 0x7c, 0x5c, 0x60, 0xde, + 0x3d, 0x65, 0xdd, 0x95, 0x56, 0x19, 0xb6, 0x7d, 0x65, 0xd1, 0x87, 0xa0, 0x8d, 0x83, 0x70, 0x4c, + 0x09, 0x13, 0x63, 0x3c, 0x31, 0x76, 0x7a, 0x5a, 0xbf, 0xe5, 0x3d, 0xce, 0x52, 0xab, 0x35, 0xf2, + 0x86, 0xc7, 0x84, 0x89, 0xd1, 0xd1, 0x2a, 0xb5, 0x74, 0x19, 0x2e, 0x91, 0xb6, 0xdf, 0xc2, 0x41, + 0x58, 0x00, 0x13, 0x7d, 0x04, 0xda, 0xa5, 0x83, 0x65, 0x34, 0x6b, 0x3e, 0x79, 0x98, 0xb3, 0x6f, + 0x70, 0x32, 0xf3, 0xb6, 0xf3, 0xe1, 0x7c, 0x10, 0xae, 0x17, 0xb8, 0xfe, 0x09, 0xdc, 0x81, 0x94, + 0xc6, 0x38, 0x2c, 0x36, 0x53, 0x09, 0x6f, 0x16, 0xc2, 0x7e, 0xa5, 0xf0, 0xe5, 0x65, 0xa2, 0xa4, + 0xbd, 0x0d, 0x37, 0x97, 0xf9, 0xe1, 0xf6, 0xb7, 0x1f, 0x56, 0xc3, 0x7b, 0x7d, 0x9e, 0x99, 0xda, + 0x45, 0x66, 0x6a, 0x7f, 0x32, 0x53, 0xfb, 0xbe, 0x34, 0x1b, 0x17, 0x4b, 0xb3, 0xf1, 0x7b, 0x69, + 0x36, 0x3e, 0x3a, 0x11, 0x16, 0xd3, 0x45, 0xe0, 0x84, 0x64, 0xee, 0xca, 0xae, 0xa7, 0x31, 0x0c, + 0xb8, 0x7a, 0x76, 0xbf, 0x5e, 0x5e, 0x12, 0x71, 0x46, 0x11, 0x0f, 0x9a, 0xc5, 0x95, 0x78, 0xfe, + 0x37, 0x00, 0x00, 0xff, 0xff, 0x2d, 0x8b, 0xfc, 0x9e, 0x8f, 0x04, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { diff --git a/x/profiles/types/keys.go b/x/profiles/types/keys.go index 4f0a96996d..2419fc91db 100644 --- a/x/profiles/types/keys.go +++ b/x/profiles/types/keys.go @@ -90,18 +90,20 @@ func RelationshipsStoreKey(user, subspace, recipient string) []byte { return append(UserRelationshipsSubspacePrefix(user, subspace), []byte(recipient)...) } -// BLockPrefix returns the store prefix used to store the blocks created by the given blocker +// BlockerPrefix returns the store prefix used to store the blocks created by the given blocker func BlockerPrefix(blocker string) []byte { return append(UsersBlocksStorePrefix, []byte(blocker)...) } -// BlockerSubspacePrefix returns the store prefix used to store the blocks that the given blocker has created inside the specified subspace +// BlockerSubspacePrefix returns the store prefix used to store the blocks that the given blocker +// has created inside the specified subspace func BlockerSubspacePrefix(blocker string, subspace string) []byte { return append(BlockerPrefix(blocker), []byte(subspace)...) } -// UsersBlocksStoreKey returns the store key used to save the block made by the given blocker, inside the specified subspace and towards the given blocked user -func UsersBlocksStoreKey(blocker string, subspace string, blockedUser string) []byte { +// UserBlockStoreKey returns the store key used to save the block made by the given blocker, +// inside the specified subspace and towards the given blocked user +func UserBlockStoreKey(blocker string, subspace string, blockedUser string) []byte { return append(BlockerSubspacePrefix(blocker, subspace), []byte(blockedUser)...) } diff --git a/x/profiles/types/models_app_links.pb.go b/x/profiles/types/models_app_links.pb.go index 16862193f7..c376fd821c 100644 --- a/x/profiles/types/models_app_links.pb.go +++ b/x/profiles/types/models_app_links.pb.go @@ -118,48 +118,6 @@ func (m *ApplicationLink) XXX_DiscardUnknown() { var xxx_messageInfo_ApplicationLink proto.InternalMessageInfo -func (m *ApplicationLink) GetUser() string { - if m != nil { - return m.User - } - return "" -} - -func (m *ApplicationLink) GetData() Data { - if m != nil { - return m.Data - } - return Data{} -} - -func (m *ApplicationLink) GetState() ApplicationLinkState { - if m != nil { - return m.State - } - return ApplicationLinkStateInitialized -} - -func (m *ApplicationLink) GetOracleRequest() OracleRequest { - if m != nil { - return m.OracleRequest - } - return OracleRequest{} -} - -func (m *ApplicationLink) GetResult() *Result { - if m != nil { - return m.Result - } - return nil -} - -func (m *ApplicationLink) GetCreationTime() time.Time { - if m != nil { - return m.CreationTime - } - return time.Time{} -} - // Data contains the data associated to a specific user of a // generic centralized application type Data struct { @@ -202,20 +160,6 @@ func (m *Data) XXX_DiscardUnknown() { var xxx_messageInfo_Data proto.InternalMessageInfo -func (m *Data) GetApplication() string { - if m != nil { - return m.Application - } - return "" -} - -func (m *Data) GetUsername() string { - if m != nil { - return m.Username - } - return "" -} - // OracleRequest represents a generic oracle request used to // verify the ownership of a centralized application account type OracleRequest struct { @@ -262,34 +206,6 @@ func (m *OracleRequest) XXX_DiscardUnknown() { var xxx_messageInfo_OracleRequest proto.InternalMessageInfo -func (m *OracleRequest) GetID() int64 { - if m != nil { - return m.ID - } - return 0 -} - -func (m *OracleRequest) GetOracleScriptID() int64 { - if m != nil { - return m.OracleScriptID - } - return 0 -} - -func (m *OracleRequest) GetCallData() OracleRequest_CallData { - if m != nil { - return m.CallData - } - return OracleRequest_CallData{} -} - -func (m *OracleRequest) GetClientID() string { - if m != nil { - return m.ClientID - } - return "" -} - // CallData contains the data sent to a single oracle request in order to // verify the ownership of a centralized application by a Desmos profile type OracleRequest_CallData struct { @@ -479,20 +395,6 @@ func (m *Result_Success) XXX_DiscardUnknown() { var xxx_messageInfo_Result_Success proto.InternalMessageInfo -func (m *Result_Success) GetValue() string { - if m != nil { - return m.Value - } - return "" -} - -func (m *Result_Success) GetSignature() string { - if m != nil { - return m.Signature - } - return "" -} - // Failed is the result of an application link that has not been verified // successfully type Result_Failed struct { @@ -533,13 +435,6 @@ func (m *Result_Failed) XXX_DiscardUnknown() { var xxx_messageInfo_Result_Failed proto.InternalMessageInfo -func (m *Result_Failed) GetError() string { - if m != nil { - return m.Error - } - return "" -} - func init() { proto.RegisterEnum("desmos.profiles.v1beta1.ApplicationLinkState", ApplicationLinkState_name, ApplicationLinkState_value) proto.RegisterType((*ApplicationLink)(nil), "desmos.profiles.v1beta1.ApplicationLink") @@ -556,67 +451,68 @@ func init() { } var fileDescriptor_33caa1214beac081 = []byte{ - // 956 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x95, 0xcf, 0x6f, 0xe3, 0x44, + // 964 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x96, 0x41, 0x6f, 0xe3, 0x44, 0x14, 0xc7, 0xe3, 0x24, 0xcd, 0x36, 0xd3, 0x6d, 0x1b, 0xa6, 0x85, 0x8d, 0x2c, 0x35, 0x36, 0x2e, - 0x2a, 0x05, 0x54, 0x5b, 0x2d, 0x12, 0x42, 0x2b, 0x21, 0x11, 0x27, 0xae, 0xea, 0xa5, 0xb4, 0xd5, - 0xc4, 0xdd, 0x95, 0xf6, 0x62, 0x4d, 0xec, 0x49, 0xb0, 0xea, 0xc4, 0xc1, 0x3f, 0x16, 0x16, 0x89, - 0x3b, 0xea, 0x69, 0xff, 0x81, 0x4a, 0x48, 0xfc, 0x15, 0x9c, 0x10, 0xb7, 0x3d, 0xee, 0x91, 0x93, - 0x41, 0xe9, 0x85, 0x73, 0x4e, 0x1c, 0x91, 0x67, 0xc6, 0x49, 0xca, 0x6e, 0x68, 0xa5, 0xbd, 0x39, - 0xf3, 0xbe, 0xef, 0xf3, 0xde, 0xbc, 0xef, 0xd3, 0x04, 0xa8, 0x2e, 0x89, 0x06, 0x41, 0xa4, 0x8d, - 0xc2, 0xa0, 0xe7, 0xf9, 0x24, 0xd2, 0x9e, 0xed, 0x77, 0x49, 0x8c, 0xf7, 0xb5, 0x41, 0xe0, 0x12, - 0x3f, 0xb2, 0xf1, 0x68, 0x64, 0xfb, 0xde, 0xf0, 0x22, 0x52, 0x47, 0x61, 0x10, 0x07, 0xf0, 0x01, - 0xd3, 0xab, 0xb9, 0x5e, 0xe5, 0x7a, 0x71, 0xb3, 0x1f, 0xf4, 0x03, 0xaa, 0xd1, 0xb2, 0x2f, 0x26, - 0x17, 0xa5, 0x7e, 0x10, 0xf4, 0x7d, 0xa2, 0xd1, 0x5f, 0xdd, 0xa4, 0xa7, 0xc5, 0xde, 0x80, 0x44, - 0x31, 0x1e, 0x8c, 0x98, 0x40, 0xf9, 0xa7, 0x04, 0xd6, 0x9b, 0xa3, 0x91, 0xef, 0x39, 0x38, 0xf6, - 0x82, 0xe1, 0xb1, 0x37, 0xbc, 0x80, 0xdb, 0xa0, 0x9c, 0x44, 0x24, 0xac, 0x0b, 0xb2, 0xb0, 0x5b, - 0xd5, 0xd7, 0x27, 0xa9, 0xb4, 0xf2, 0x1c, 0x0f, 0xfc, 0x87, 0x4a, 0x76, 0xaa, 0x20, 0x1a, 0x84, - 0x87, 0xa0, 0xec, 0xe2, 0x18, 0xd7, 0x8b, 0xb2, 0xb0, 0xbb, 0x72, 0xb0, 0xa5, 0x2e, 0xe8, 0x4b, - 0x6d, 0xe3, 0x18, 0xeb, 0x1b, 0x2f, 0x53, 0xa9, 0x30, 0xe3, 0x64, 0x89, 0x0a, 0xa2, 0xf9, 0xf0, - 0x1c, 0x2c, 0x45, 0x31, 0x8e, 0x49, 0xbd, 0x24, 0x0b, 0xbb, 0x6b, 0x07, 0x7b, 0x0b, 0x41, 0xff, - 0xe9, 0xb2, 0x93, 0x25, 0xe9, 0xb5, 0x49, 0x2a, 0xdd, 0x67, 0x50, 0x4a, 0x51, 0x10, 0xa3, 0x41, - 0x1f, 0xac, 0x05, 0x21, 0x76, 0x7c, 0x62, 0x87, 0xe4, 0xdb, 0x84, 0x44, 0x71, 0xbd, 0x4c, 0x1b, - 0xdd, 0x59, 0xc8, 0x3f, 0xa5, 0x72, 0xc4, 0xd4, 0xfa, 0x16, 0xef, 0xf8, 0x5d, 0x06, 0xbf, 0xc9, - 0x52, 0xd0, 0x6a, 0x30, 0xaf, 0x86, 0x8f, 0x40, 0x25, 0x24, 0x51, 0xe2, 0xc7, 0xf5, 0x25, 0x5a, - 0x45, 0x5a, 0x58, 0x05, 0x51, 0x99, 0xfe, 0xce, 0x24, 0x95, 0x56, 0x19, 0x9a, 0x25, 0x2a, 0x88, - 0x13, 0x20, 0x06, 0xab, 0x4e, 0x48, 0xe8, 0x3d, 0xed, 0xcc, 0xad, 0x7a, 0x85, 0x22, 0x45, 0x95, - 0x59, 0xa9, 0xe6, 0x56, 0xaa, 0x56, 0x6e, 0xa5, 0x2e, 0xf3, 0x66, 0x37, 0x19, 0xf1, 0x46, 0xba, - 0xf2, 0xe2, 0x4f, 0x49, 0x40, 0xf7, 0xf3, 0xb3, 0x2c, 0xe9, 0x61, 0xf9, 0xef, 0x9f, 0x25, 0x41, - 0xf9, 0x0e, 0x94, 0x33, 0x73, 0xe0, 0xe7, 0x60, 0x05, 0xcf, 0x66, 0xcb, 0x5d, 0x7f, 0x6f, 0x92, - 0x4a, 0x90, 0xe1, 0xe6, 0x82, 0x0a, 0x9a, 0x97, 0x42, 0x0d, 0x2c, 0x67, 0xbb, 0x30, 0xc4, 0x03, - 0x42, 0xf7, 0xa0, 0xaa, 0x6f, 0x4c, 0x52, 0x69, 0x7d, 0xb6, 0x2c, 0x59, 0x44, 0x41, 0x53, 0x11, - 0x2f, 0xfc, 0x6b, 0x09, 0xac, 0xde, 0x98, 0x36, 0xdc, 0x06, 0x45, 0xcf, 0xa5, 0x95, 0x4b, 0xfa, - 0xc6, 0x38, 0x95, 0x8a, 0x66, 0x7b, 0x92, 0x4a, 0x55, 0x06, 0xf2, 0x5c, 0x05, 0x15, 0x3d, 0x17, - 0x3e, 0x01, 0x35, 0x6e, 0x43, 0xe4, 0x84, 0xde, 0x28, 0xb6, 0x3d, 0x97, 0x56, 0x2d, 0xe9, 0x7b, - 0xe3, 0x54, 0x5a, 0x63, 0xc4, 0x0e, 0x0d, 0xd1, 0xf4, 0x07, 0x37, 0xac, 0x9b, 0xe6, 0x28, 0x88, - 0x6f, 0x06, 0x97, 0xba, 0xb0, 0x07, 0xaa, 0x0e, 0xf6, 0x7d, 0x9b, 0xee, 0x73, 0x89, 0x4e, 0x5b, - 0xbb, 0xdb, 0x9a, 0xa8, 0x2d, 0xec, 0xfb, 0x74, 0xc3, 0xeb, 0xdc, 0x82, 0x1a, 0xb7, 0x20, 0xe7, - 0x29, 0x68, 0xd9, 0xe1, 0x1a, 0xf8, 0x05, 0xa8, 0x3a, 0xbe, 0x47, 0x86, 0xb4, 0xf3, 0x32, 0x9d, - 0x97, 0x3c, 0x4e, 0xa5, 0xe5, 0x16, 0x3d, 0xa4, 0x3d, 0xe7, 0xe9, 0xb9, 0x2c, 0x4b, 0x67, 0x51, - 0x57, 0xfc, 0x11, 0x2c, 0xe7, 0xe5, 0xde, 0xc2, 0xb3, 0xfd, 0xf9, 0xcb, 0x32, 0xd3, 0x36, 0xff, - 0xbf, 0x6f, 0xe6, 0x1a, 0xf7, 0xee, 0xf7, 0x22, 0xa8, 0xb0, 0x1d, 0x86, 0x2d, 0x70, 0x2f, 0x4a, - 0x1c, 0x87, 0x44, 0x11, 0xad, 0xbf, 0x72, 0xf0, 0xe1, 0x2d, 0x5b, 0xaf, 0x76, 0x98, 0xfc, 0xa8, - 0x80, 0xf2, 0x4c, 0xf8, 0x25, 0xa8, 0xf4, 0xb0, 0xe7, 0x13, 0x97, 0x3f, 0x24, 0x3b, 0xb7, 0x31, - 0x0e, 0xa9, 0xfa, 0xa8, 0x80, 0x78, 0x9e, 0x78, 0x01, 0xee, 0x71, 0x2e, 0xdc, 0x01, 0x4b, 0xcf, - 0xb0, 0x9f, 0x10, 0x3e, 0x8f, 0xb9, 0xc7, 0x81, 0x1e, 0x2b, 0x88, 0x85, 0xe1, 0x01, 0xa8, 0x46, - 0x5e, 0x7f, 0x88, 0xe3, 0x24, 0x24, 0xaf, 0xcf, 0x60, 0x1a, 0x52, 0xd0, 0x4c, 0xc6, 0xae, 0x2f, - 0x7e, 0x06, 0x2a, 0xac, 0x81, 0xac, 0x16, 0x09, 0xc3, 0x20, 0x7c, 0xbd, 0x16, 0x3d, 0x56, 0x10, - 0x0b, 0xcf, 0x0f, 0x4f, 0x5f, 0x02, 0xa5, 0x28, 0x19, 0x7c, 0xfc, 0x5b, 0x09, 0x6c, 0xbe, 0xe9, - 0x35, 0x83, 0x4f, 0x80, 0xda, 0x3c, 0x3b, 0x3b, 0x36, 0x5b, 0x4d, 0xcb, 0x3c, 0x3d, 0xb1, 0x8f, - 0xcd, 0x93, 0xaf, 0xec, 0x8e, 0xd5, 0xb4, 0x0c, 0xdb, 0x3c, 0x31, 0x2d, 0xb3, 0x79, 0x6c, 0x3e, - 0x35, 0xda, 0xf6, 0xf9, 0x49, 0xe7, 0xcc, 0x68, 0x99, 0x87, 0xa6, 0xd1, 0xae, 0x15, 0xc4, 0xed, - 0xcb, 0x2b, 0x59, 0x7a, 0x13, 0xcd, 0x1c, 0x7a, 0xb1, 0x87, 0x7d, 0xef, 0x07, 0xe2, 0x42, 0x0b, - 0x7c, 0xb2, 0x00, 0xfc, 0xd8, 0x40, 0xe6, 0x61, 0x7e, 0xde, 0xb1, 0x9a, 0xc8, 0x32, 0xda, 0x35, - 0x61, 0x4a, 0x9d, 0xd2, 0x1e, 0x93, 0xd0, 0xeb, 0xf1, 0x12, 0x9d, 0x18, 0x87, 0x31, 0x71, 0xe1, - 0x19, 0xf8, 0xe8, 0x2e, 0x54, 0x03, 0xa1, 0x53, 0x54, 0x2b, 0x8a, 0xef, 0x5f, 0x5e, 0xc9, 0x5b, - 0x8b, 0x98, 0x46, 0x36, 0xac, 0x3b, 0xf7, 0x79, 0xde, 0x6a, 0x19, 0x9d, 0x4e, 0xad, 0x74, 0x4b, - 0x9f, 0x7c, 0x2d, 0x1e, 0x01, 0x79, 0x01, 0xd5, 0x32, 0xbf, 0x36, 0xda, 0xf6, 0xe9, 0xb9, 0x55, - 0x2b, 0x8b, 0x1f, 0x5c, 0x5e, 0xc9, 0xf2, 0x22, 0x54, 0xf6, 0x6c, 0xba, 0xa7, 0x49, 0x2c, 0x96, - 0x7f, 0xfa, 0xa5, 0x51, 0xd0, 0x8f, 0x5e, 0x8e, 0x1b, 0xc2, 0xab, 0x71, 0x43, 0xf8, 0x6b, 0xdc, - 0x10, 0x5e, 0x5c, 0x37, 0x0a, 0xaf, 0xae, 0x1b, 0x85, 0x3f, 0xae, 0x1b, 0x85, 0xa7, 0x6a, 0xdf, - 0x8b, 0xbf, 0x49, 0xba, 0xaa, 0x13, 0x0c, 0x34, 0xb6, 0xc9, 0x7b, 0x3e, 0xee, 0x46, 0xfc, 0x5b, - 0xfb, 0x7e, 0xf6, 0x47, 0x1f, 0x3f, 0x1f, 0x91, 0xa8, 0x5b, 0xa1, 0xcf, 0xf9, 0xa7, 0xff, 0x06, - 0x00, 0x00, 0xff, 0xff, 0x1f, 0x97, 0x8d, 0xcb, 0x08, 0x08, 0x00, 0x00, + 0x2a, 0x05, 0x54, 0x5b, 0x2d, 0x17, 0x54, 0x09, 0x89, 0x38, 0x71, 0x55, 0x2f, 0xa5, 0xad, 0x26, + 0xee, 0xae, 0xb4, 0x17, 0x6b, 0x62, 0x4f, 0x82, 0xb5, 0x4e, 0x1c, 0xec, 0xf1, 0x8a, 0x05, 0x71, + 0x5f, 0xf5, 0xb4, 0x5f, 0xa0, 0xd2, 0x4a, 0x7c, 0x0e, 0xc4, 0x75, 0x2f, 0x48, 0x7b, 0xe4, 0x64, + 0x50, 0x7a, 0xe1, 0x9c, 0x33, 0x07, 0xe4, 0x19, 0x3b, 0x49, 0xd9, 0x0d, 0xad, 0xc4, 0x6d, 0x3a, + 0xef, 0xff, 0x7e, 0xef, 0xf9, 0xfd, 0x5f, 0x47, 0x01, 0xaa, 0x4b, 0xa2, 0x41, 0x10, 0x69, 0xa3, + 0x30, 0xe8, 0x79, 0x3e, 0x89, 0xb4, 0x67, 0xfb, 0x5d, 0x42, 0xf1, 0xbe, 0x36, 0x08, 0x5c, 0xe2, + 0x47, 0x36, 0x1e, 0x8d, 0x6c, 0xdf, 0x1b, 0x3e, 0x8d, 0xd4, 0x51, 0x18, 0xd0, 0x00, 0x3e, 0xe0, + 0x7a, 0x35, 0xd7, 0xab, 0x99, 0x5e, 0xdc, 0xec, 0x07, 0xfd, 0x80, 0x69, 0xb4, 0xf4, 0xc4, 0xe5, + 0xa2, 0xd4, 0x0f, 0x82, 0xbe, 0x4f, 0x34, 0xf6, 0x57, 0x37, 0xee, 0x69, 0xd4, 0x1b, 0x90, 0x88, + 0xe2, 0xc1, 0x88, 0x0b, 0x94, 0xbf, 0x4b, 0x60, 0xbd, 0x39, 0x1a, 0xf9, 0x9e, 0x83, 0xa9, 0x17, + 0x0c, 0x4f, 0xbc, 0xe1, 0x53, 0xb8, 0x0d, 0xca, 0x71, 0x44, 0xc2, 0xba, 0x20, 0x0b, 0xbb, 0x55, + 0x7d, 0x7d, 0x92, 0x48, 0x2b, 0xcf, 0xf1, 0xc0, 0x3f, 0x54, 0xd2, 0x5b, 0x05, 0xb1, 0x20, 0x3c, + 0x02, 0x65, 0x17, 0x53, 0x5c, 0x2f, 0xca, 0xc2, 0xee, 0xca, 0xc1, 0x96, 0xba, 0xa0, 0x2f, 0xb5, + 0x8d, 0x29, 0xd6, 0x37, 0x5e, 0x27, 0x52, 0x61, 0xc6, 0x49, 0x13, 0x15, 0xc4, 0xf2, 0xe1, 0x05, + 0x58, 0x8a, 0x28, 0xa6, 0xa4, 0x5e, 0x92, 0x85, 0xdd, 0xb5, 0x83, 0xbd, 0x85, 0xa0, 0x7f, 0x75, + 0xd9, 0x49, 0x93, 0xf4, 0xda, 0x24, 0x91, 0xee, 0x73, 0x28, 0xa3, 0x28, 0x88, 0xd3, 0xa0, 0x0f, + 0xd6, 0x82, 0x10, 0x3b, 0x3e, 0xb1, 0x43, 0xf2, 0x5d, 0x4c, 0x22, 0x5a, 0x2f, 0xb3, 0x46, 0x77, + 0x16, 0xf2, 0xcf, 0x98, 0x1c, 0x71, 0xb5, 0xbe, 0x95, 0x75, 0xfc, 0x3e, 0x87, 0xdf, 0x64, 0x29, + 0x68, 0x35, 0x98, 0x57, 0xc3, 0x87, 0xa0, 0x12, 0x92, 0x28, 0xf6, 0x69, 0x7d, 0x89, 0x55, 0x91, + 0x16, 0x56, 0x41, 0x4c, 0xa6, 0xbf, 0x37, 0x49, 0xa4, 0x55, 0x8e, 0xe6, 0x89, 0x0a, 0xca, 0x08, + 0x10, 0x83, 0x55, 0x27, 0x24, 0xec, 0x3b, 0xed, 0xd4, 0xad, 0x7a, 0x85, 0x21, 0x45, 0x95, 0x5b, + 0xa9, 0xe6, 0x56, 0xaa, 0x56, 0x6e, 0xa5, 0x2e, 0x67, 0xcd, 0x6e, 0x72, 0xe2, 0x8d, 0x74, 0xe5, + 0xe5, 0x1f, 0x92, 0x80, 0xee, 0xe7, 0x77, 0x69, 0xd2, 0xe1, 0xf2, 0x8b, 0x57, 0x52, 0xe1, 0xaf, + 0x57, 0x92, 0xa0, 0xfc, 0x08, 0xca, 0xa9, 0x41, 0xf0, 0x0b, 0xb0, 0x82, 0x67, 0xf3, 0xcd, 0x9c, + 0xff, 0x60, 0x92, 0x48, 0x90, 0x23, 0xe7, 0x82, 0x0a, 0x9a, 0x97, 0x42, 0x0d, 0x2c, 0xa7, 0xfb, + 0x30, 0xc4, 0x03, 0xc2, 0x76, 0xa1, 0xaa, 0x6f, 0x4c, 0x12, 0x69, 0x7d, 0xb6, 0x30, 0x69, 0x44, + 0x41, 0x53, 0xd1, 0x5c, 0xf1, 0x5f, 0x4a, 0x60, 0xf5, 0xc6, 0xd4, 0xe1, 0x36, 0x28, 0x7a, 0x2e, + 0xab, 0x5e, 0xd2, 0x37, 0xc6, 0x89, 0x54, 0x34, 0xdb, 0x93, 0x44, 0xaa, 0x72, 0x98, 0xe7, 0x2a, + 0xa8, 0xe8, 0xb9, 0xf0, 0x31, 0xa8, 0x65, 0x76, 0x44, 0x4e, 0xe8, 0x8d, 0xa8, 0xed, 0xb9, 0xac, + 0x72, 0x49, 0xdf, 0x1b, 0x27, 0xd2, 0x1a, 0x27, 0x76, 0x58, 0x88, 0xa5, 0x3f, 0xb8, 0x61, 0xe1, + 0x34, 0x47, 0x41, 0xd9, 0x86, 0x64, 0x52, 0x17, 0xf6, 0x40, 0xd5, 0xc1, 0xbe, 0x6f, 0xb3, 0xbd, + 0x2e, 0xb1, 0xa9, 0x6b, 0x77, 0x5b, 0x17, 0xb5, 0x85, 0x7d, 0x9f, 0x6d, 0x7a, 0x3d, 0xb3, 0xa2, + 0x96, 0x59, 0x91, 0xf3, 0x14, 0xb4, 0xec, 0x64, 0x1a, 0xf8, 0x25, 0xa8, 0x3a, 0xbe, 0x47, 0x86, + 0xac, 0xf3, 0x32, 0x9b, 0x99, 0x3c, 0x4e, 0xa4, 0xe5, 0x16, 0xbb, 0x64, 0x3d, 0xe7, 0xe9, 0xb9, + 0x2c, 0x4d, 0xe7, 0x51, 0x57, 0xfc, 0x09, 0x2c, 0xe7, 0xe5, 0xfe, 0x87, 0x6f, 0xfb, 0xf3, 0x1f, + 0xcb, 0x8d, 0xdb, 0xfc, 0xef, 0xbe, 0x0f, 0xcb, 0xa9, 0x6b, 0x73, 0xfe, 0xfd, 0x56, 0x04, 0x15, + 0xbe, 0xcf, 0xb0, 0x05, 0xee, 0x45, 0xb1, 0xe3, 0x90, 0x28, 0x62, 0x3d, 0xac, 0x1c, 0x7c, 0x7c, + 0xcb, 0x7f, 0x80, 0xda, 0xe1, 0xf2, 0xe3, 0x02, 0xca, 0x33, 0xe1, 0x57, 0xa0, 0xd2, 0xc3, 0x9e, + 0x4f, 0xdc, 0xec, 0x51, 0xd9, 0xb9, 0x8d, 0x71, 0xc4, 0xd4, 0xc7, 0x05, 0x94, 0xe5, 0x89, 0x01, + 0xb8, 0x97, 0x71, 0xe1, 0x0e, 0x58, 0x7a, 0x86, 0xfd, 0x98, 0x64, 0x33, 0x99, 0x7b, 0x28, 0xd8, + 0xb5, 0x82, 0x78, 0x18, 0x1e, 0x80, 0x6a, 0xe4, 0xf5, 0x87, 0x98, 0xc6, 0x21, 0x79, 0x7b, 0x0e, + 0xd3, 0x90, 0x82, 0x66, 0xb2, 0xd9, 0x08, 0xc4, 0x43, 0x50, 0xe1, 0x4d, 0xa4, 0xf5, 0x48, 0x18, + 0x06, 0xe1, 0xdb, 0xf5, 0xd8, 0xb5, 0x82, 0x78, 0x78, 0x96, 0x3b, 0x3b, 0xe9, 0x4b, 0xa0, 0x14, + 0xc5, 0x83, 0x4f, 0x7f, 0x2d, 0x81, 0xcd, 0x77, 0xbd, 0x72, 0xf0, 0x31, 0x50, 0x9b, 0xe7, 0xe7, + 0x27, 0x66, 0xab, 0x69, 0x99, 0x67, 0xa7, 0xf6, 0x89, 0x79, 0xfa, 0xb5, 0xdd, 0xb1, 0x9a, 0x96, + 0x61, 0x9b, 0xa7, 0xa6, 0x65, 0x36, 0x4f, 0xcc, 0x27, 0x46, 0xdb, 0xbe, 0x38, 0xed, 0x9c, 0x1b, + 0x2d, 0xf3, 0xc8, 0x34, 0xda, 0xb5, 0x82, 0xb8, 0x7d, 0x79, 0x25, 0x4b, 0xef, 0xa2, 0x99, 0x43, + 0x8f, 0x7a, 0xd8, 0xf7, 0x7e, 0x20, 0x2e, 0xb4, 0xc0, 0x67, 0x0b, 0xc0, 0x8f, 0x0c, 0x64, 0x1e, + 0xe5, 0xf7, 0x1d, 0xab, 0x89, 0x2c, 0xa3, 0x5d, 0x13, 0xa6, 0xd4, 0x29, 0xed, 0x11, 0x09, 0xbd, + 0x5e, 0x56, 0xa2, 0x43, 0x71, 0x48, 0x89, 0x0b, 0xcf, 0xc1, 0x27, 0x77, 0xa1, 0x1a, 0x08, 0x9d, + 0xa1, 0x5a, 0x51, 0xfc, 0xf0, 0xf2, 0x4a, 0xde, 0x5a, 0xc4, 0x34, 0xd2, 0xa1, 0xdd, 0xb9, 0xcf, + 0x8b, 0x56, 0xcb, 0xe8, 0x74, 0x6a, 0xa5, 0x5b, 0xfa, 0xcc, 0x56, 0xe4, 0x21, 0x90, 0x17, 0x50, + 0x2d, 0xf3, 0x1b, 0xa3, 0x6d, 0x9f, 0x5d, 0x58, 0xb5, 0xb2, 0xf8, 0xd1, 0xe5, 0x95, 0x2c, 0x2f, + 0x42, 0xa5, 0xcf, 0xa9, 0x7b, 0x16, 0x53, 0xb1, 0xfc, 0xe2, 0xe7, 0x46, 0x41, 0x3f, 0x7e, 0x3d, + 0x6e, 0x08, 0x6f, 0xc6, 0x0d, 0xe1, 0xcf, 0x71, 0x43, 0x78, 0x79, 0xdd, 0x28, 0xbc, 0xb9, 0x6e, + 0x14, 0x7e, 0xbf, 0x6e, 0x14, 0x9e, 0xa8, 0x7d, 0x8f, 0x7e, 0x1b, 0x77, 0x55, 0x27, 0x18, 0x68, + 0x7c, 0xab, 0xf7, 0x7c, 0xdc, 0x8d, 0xb2, 0xb3, 0xf6, 0xfd, 0xec, 0x07, 0x00, 0x7d, 0x3e, 0x22, + 0x51, 0xb7, 0xc2, 0x9e, 0xf9, 0xcf, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x3e, 0xf3, 0xfe, 0x9f, + 0x20, 0x08, 0x00, 0x00, } func (this *ApplicationLink) Equal(that interface{}) bool { diff --git a/x/profiles/types/models_chain_links.go b/x/profiles/types/models_chain_links.go index 911a97c190..c40e9a6b3a 100644 --- a/x/profiles/types/models_chain_links.go +++ b/x/profiles/types/models_chain_links.go @@ -1,11 +1,15 @@ package types import ( + "bytes" "encoding/hex" "fmt" "strings" "time" + "github.com/cosmos/cosmos-sdk/types/bech32" + "github.com/tendermint/tendermint/crypto/tmhash" + "github.com/gogo/protobuf/proto" "github.com/mr-tron/base58" @@ -66,7 +70,7 @@ func (p Proof) Validate() error { // Verify verifies the signature using the given plain text and public key. // It returns and error if something is invalid. -func (p Proof) Verify(unpacker codectypes.AnyUnpacker) error { +func (p Proof) Verify(unpacker codectypes.AnyUnpacker, address AddressData) error { var pubkey cryptotypes.PubKey err := unpacker.UnpackAny(p.PubKey, &pubkey) if err != nil { @@ -78,6 +82,15 @@ func (p Proof) Verify(unpacker codectypes.AnyUnpacker) error { return fmt.Errorf("failed to verify the signature") } + valid, err := address.VerifyPubKey(pubkey) + if err != nil { + return err + } + + if !valid { + return fmt.Errorf("invalid address and public key combination provided") + } + return nil } @@ -96,8 +109,11 @@ type AddressData interface { // Validate checks the validity of the AddressData Validate() error - // GetAddress returns the address value - GetAddress() string + // GetValue returns the address value + GetValue() string + + // VerifyPubKey verifies that the given public key is associated with this address data + VerifyPubKey(key cryptotypes.PubKey) (bool, error) } // -------------------------------------------------------------------------------------------------------------------- @@ -127,11 +143,20 @@ func (b Bech32Address) Validate() error { return nil } -// GetAddress implements AddressData -func (b Bech32Address) GetAddress() string { +// GetValue implements AddressData +func (b Bech32Address) GetValue() string { return b.Value } +// VerifyPubKey implements AddressData +func (b Bech32Address) VerifyPubKey(key cryptotypes.PubKey) (bool, error) { + _, bz, err := bech32.DecodeAndConvert(b.Value) + if err != nil { + return false, err + } + return bytes.Equal(bz, key.Address().Bytes()), nil +} + // -------------------------------------------------------------------------------------------------------------------- var _ AddressData = &Base58Address{} @@ -154,11 +179,17 @@ func (b Base58Address) Validate() error { return nil } -// GetAddress implements AddressData -func (b Base58Address) GetAddress() string { +// GetValue implements AddressData +func (b Base58Address) GetValue() string { return b.Value } +// VerifyPubKey implements AddressData +func (b Base58Address) VerifyPubKey(key cryptotypes.PubKey) (bool, error) { + bz, err := base58.Decode(b.Value) + return bytes.Equal(tmhash.SumTruncated(bz), key.Address().Bytes()), err +} + // -------------------------------------------------------------------------------------------------------------------- // UnpackAddressData deserializes the given any type value as an address data using the provided unpacker @@ -188,6 +219,11 @@ func NewChainLink(user string, address AddressData, proof Proof, chainConfig Cha } } +// GetAddressData returns the AddressData associated with this chain link +func (link ChainLink) GetAddressData() AddressData { + return link.Address.GetCachedValue().(AddressData) +} + // Validate checks the validity of the ChainLink func (link ChainLink) Validate() error { if _, err := sdk.AccAddressFromBech32(link.User); err != nil { diff --git a/x/profiles/types/models_chain_links.pb.go b/x/profiles/types/models_chain_links.pb.go index b6dda723b6..0f08bf66d2 100644 --- a/x/profiles/types/models_chain_links.pb.go +++ b/x/profiles/types/models_chain_links.pb.go @@ -78,41 +78,6 @@ func (m *ChainLink) XXX_DiscardUnknown() { var xxx_messageInfo_ChainLink proto.InternalMessageInfo -func (m *ChainLink) GetUser() string { - if m != nil { - return m.User - } - return "" -} - -func (m *ChainLink) GetAddress() *types.Any { - if m != nil { - return m.Address - } - return nil -} - -func (m *ChainLink) GetProof() Proof { - if m != nil { - return m.Proof - } - return Proof{} -} - -func (m *ChainLink) GetChainConfig() ChainConfig { - if m != nil { - return m.ChainConfig - } - return ChainConfig{} -} - -func (m *ChainLink) GetCreationTime() time.Time { - if m != nil { - return m.CreationTime - } - return time.Time{} -} - // ChainConfig contains the data of the chain with which the link is made. type ChainConfig struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty" yaml:"name"` @@ -151,13 +116,6 @@ func (m *ChainConfig) XXX_DiscardUnknown() { var xxx_messageInfo_ChainConfig proto.InternalMessageInfo -func (m *ChainConfig) GetName() string { - if m != nil { - return m.Name - } - return "" -} - // Proof contains all the data used to verify a signature when linking an // account to a profile type Proof struct { @@ -203,27 +161,6 @@ func (m *Proof) XXX_DiscardUnknown() { var xxx_messageInfo_Proof proto.InternalMessageInfo -func (m *Proof) GetPubKey() *types.Any { - if m != nil { - return m.PubKey - } - return nil -} - -func (m *Proof) GetSignature() string { - if m != nil { - return m.Signature - } - return "" -} - -func (m *Proof) GetPlainText() string { - if m != nil { - return m.PlainText - } - return "" -} - // Bech32Address represents a Bech32-encoded address type Bech32Address struct { // Value represents the Bech-32 encoded address value @@ -265,20 +202,6 @@ func (m *Bech32Address) XXX_DiscardUnknown() { var xxx_messageInfo_Bech32Address proto.InternalMessageInfo -func (m *Bech32Address) GetValue() string { - if m != nil { - return m.Value - } - return "" -} - -func (m *Bech32Address) GetPrefix() string { - if m != nil { - return m.Prefix - } - return "" -} - // Base58Address represents a Base58-encoded address type Base58Address struct { // Value contains the Base58-encoded address @@ -318,13 +241,6 @@ func (m *Base58Address) XXX_DiscardUnknown() { var xxx_messageInfo_Base58Address proto.InternalMessageInfo -func (m *Base58Address) GetValue() string { - if m != nil { - return m.Value - } - return "" -} - func init() { proto.RegisterType((*ChainLink)(nil), "desmos.profiles.v1beta1.ChainLink") proto.RegisterType((*ChainConfig)(nil), "desmos.profiles.v1beta1.ChainConfig") @@ -338,46 +254,46 @@ func init() { } var fileDescriptor_1c1946212735e419 = []byte{ - // 615 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x31, 0x6f, 0xd3, 0x40, - 0x14, 0x8e, 0x69, 0xd3, 0x92, 0x4b, 0x03, 0xed, 0x11, 0xd4, 0x50, 0x24, 0xbb, 0x1c, 0x08, 0x95, - 0xa1, 0x36, 0x6d, 0x41, 0x42, 0x15, 0x4b, 0x5d, 0x06, 0x04, 0x0c, 0xc5, 0xea, 0xc4, 0x12, 0x9d, - 0x9d, 0x8b, 0x6b, 0xd5, 0xf6, 0x59, 0xbe, 0x73, 0x15, 0x8b, 0x3f, 0xd1, 0x91, 0xb1, 0xe2, 0x37, - 0x30, 0x33, 0x57, 0x4c, 0x15, 0x13, 0x93, 0x41, 0xcd, 0xc2, 0xec, 0x5f, 0x80, 0xee, 0xce, 0x6e, - 0x02, 0x55, 0x06, 0xb6, 0xe7, 0xf7, 0xbe, 0xef, 0x7b, 0xef, 0xde, 0xf7, 0x64, 0xf0, 0x74, 0x40, - 0x58, 0x44, 0x99, 0x95, 0xa4, 0x74, 0x18, 0x84, 0x84, 0x59, 0x27, 0x5b, 0x2e, 0xe1, 0x78, 0xcb, - 0x8a, 0xe8, 0x80, 0x84, 0xac, 0xef, 0x1d, 0xe1, 0x20, 0xee, 0x87, 0x41, 0x7c, 0xcc, 0xcc, 0x24, - 0xa5, 0x9c, 0xc2, 0x55, 0xc5, 0x30, 0x6b, 0x86, 0x59, 0x31, 0xd6, 0xba, 0x3e, 0xf5, 0xa9, 0xc4, - 0x58, 0x22, 0x52, 0xf0, 0xb5, 0x7b, 0x3e, 0xa5, 0x7e, 0x48, 0x2c, 0xf9, 0xe5, 0x66, 0x43, 0x0b, - 0xc7, 0x79, 0x55, 0x32, 0xfe, 0x2d, 0xf1, 0x20, 0x22, 0x8c, 0xe3, 0x28, 0xa9, 0xb9, 0x1e, 0x15, - 0xad, 0xfa, 0x4a, 0x54, 0x7d, 0xa8, 0x12, 0xfa, 0x3c, 0x07, 0x5a, 0xfb, 0x62, 0xb6, 0x77, 0x41, - 0x7c, 0x0c, 0x1f, 0x82, 0xf9, 0x8c, 0x91, 0xb4, 0xa7, 0xad, 0x6b, 0x1b, 0x2d, 0xfb, 0x76, 0x59, - 0x18, 0xed, 0x1c, 0x47, 0xe1, 0x2e, 0x12, 0x59, 0xe4, 0xc8, 0x22, 0x7c, 0x0f, 0x16, 0xf1, 0x60, - 0x90, 0x12, 0xc6, 0x7a, 0x37, 0xd6, 0xb5, 0x8d, 0xf6, 0x76, 0xd7, 0x54, 0x03, 0x98, 0xf5, 0x00, - 0xe6, 0x5e, 0x9c, 0xdb, 0x0f, 0xca, 0xc2, 0xb8, 0xa5, 0xd8, 0x15, 0x1c, 0x7d, 0xfb, 0xb2, 0xd9, - 0xde, 0x53, 0xf1, 0x2b, 0xcc, 0xb1, 0x53, 0xeb, 0xc0, 0x37, 0xa0, 0x99, 0xa4, 0x94, 0x0e, 0x7b, - 0x73, 0x52, 0x50, 0x37, 0x67, 0xec, 0xc6, 0x3c, 0x10, 0x28, 0xbb, 0x7b, 0x5e, 0x18, 0x8d, 0xb2, - 0x30, 0x96, 0x94, 0xbc, 0xa4, 0x22, 0x47, 0x49, 0xc0, 0x01, 0x58, 0x52, 0xcb, 0xf6, 0x68, 0x3c, - 0x0c, 0xfc, 0xde, 0xbc, 0x94, 0x7c, 0x34, 0x53, 0x52, 0xbe, 0x7e, 0x5f, 0x62, 0xed, 0xfb, 0x95, - 0xf0, 0x1d, 0x25, 0x3c, 0xad, 0x83, 0x9c, 0xb6, 0x37, 0x41, 0x42, 0x0c, 0x3a, 0x5e, 0x4a, 0x30, - 0x0f, 0x68, 0xdc, 0x17, 0xeb, 0xee, 0x35, 0x65, 0x9b, 0xb5, 0x6b, 0xab, 0x38, 0xac, 0xbd, 0xb0, - 0xd7, 0x2b, 0xf1, 0x6e, 0x25, 0x3e, 0x4d, 0x47, 0xa7, 0x3f, 0x0d, 0xcd, 0x59, 0xaa, 0x73, 0x82, - 0xb4, 0x7b, 0xf3, 0xd3, 0x99, 0xa1, 0xfd, 0x3e, 0x33, 0x34, 0xf4, 0x12, 0xb4, 0xa7, 0xa6, 0x14, - 0x2e, 0xc5, 0x38, 0x22, 0xd7, 0x5d, 0x12, 0x59, 0xe4, 0xc8, 0xe2, 0x14, 0xfb, 0xab, 0x06, 0x9a, - 0x72, 0x6f, 0x70, 0x0f, 0x2c, 0x26, 0x99, 0xdb, 0x3f, 0x26, 0xb9, 0xe4, 0xce, 0x72, 0x0e, 0x4e, - 0x9c, 0xab, 0xe0, 0xc8, 0x59, 0x48, 0x32, 0xf7, 0x2d, 0xc9, 0xe1, 0x36, 0x68, 0xb1, 0xc0, 0x8f, - 0x31, 0xcf, 0x52, 0x22, 0xed, 0x6f, 0xd9, 0xdd, 0xb2, 0x30, 0x96, 0x15, 0xfc, 0xaa, 0x84, 0x9c, - 0x09, 0x0c, 0x3e, 0x03, 0x20, 0x09, 0xc5, 0x26, 0x39, 0x19, 0x71, 0x69, 0x71, 0xcb, 0xbe, 0x5b, - 0x16, 0xc6, 0x4a, 0xd5, 0xe3, 0xaa, 0x86, 0x9c, 0x96, 0xfc, 0x38, 0x24, 0x23, 0x3e, 0xf5, 0x80, - 0x8f, 0xa0, 0x63, 0x13, 0xef, 0x68, 0x67, 0xbb, 0xba, 0x1d, 0xf8, 0x18, 0x34, 0x4f, 0x70, 0x98, - 0xd5, 0x1b, 0x58, 0x9e, 0x9c, 0x82, 0x4c, 0x23, 0x47, 0x95, 0xe1, 0x13, 0xb0, 0x90, 0xa4, 0x64, - 0x18, 0x8c, 0xaa, 0x49, 0x57, 0xca, 0xc2, 0xe8, 0xd4, 0x37, 0x23, 0xf2, 0xe2, 0x5d, 0x32, 0xd8, - 0x5d, 0xad, 0xbb, 0x7d, 0xff, 0xfb, 0x46, 0xd1, 0x01, 0xe8, 0xd8, 0x98, 0x91, 0xe7, 0x2f, 0xfe, - 0xb3, 0xf9, 0x4c, 0x45, 0xfb, 0xf5, 0xf9, 0xa5, 0xae, 0x5d, 0x5c, 0xea, 0xda, 0xaf, 0x4b, 0x5d, - 0x3b, 0x1d, 0xeb, 0x8d, 0x8b, 0xb1, 0xde, 0xf8, 0x31, 0xd6, 0x1b, 0x1f, 0x4c, 0x3f, 0xe0, 0x47, - 0x99, 0x6b, 0x7a, 0x34, 0xb2, 0xd4, 0xb9, 0x6e, 0x86, 0xd8, 0x65, 0x55, 0x6c, 0x8d, 0x26, 0x7f, - 0x17, 0x9e, 0x27, 0x84, 0xb9, 0x0b, 0xd2, 0xb6, 0x9d, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xdb, - 0xff, 0xc3, 0x91, 0x7d, 0x04, 0x00, 0x00, + // 622 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xbf, 0x6f, 0xd3, 0x4e, + 0x14, 0x8f, 0xbf, 0x6d, 0x5a, 0xe5, 0x92, 0x7c, 0x69, 0x8f, 0x20, 0x42, 0x2b, 0xf9, 0xca, 0x81, + 0x50, 0x19, 0x6a, 0xd3, 0x16, 0x24, 0xd4, 0x89, 0xba, 0x0c, 0x08, 0x18, 0xc0, 0xea, 0xc4, 0x12, + 0x9d, 0x9d, 0x8b, 0x6b, 0xd5, 0xf6, 0x59, 0xbe, 0x73, 0x95, 0x4c, 0xac, 0x8c, 0x1d, 0x19, 0x3b, + 0xf1, 0x17, 0xf0, 0x17, 0x30, 0x55, 0x4c, 0x15, 0x13, 0x93, 0x41, 0xed, 0xc2, 0x9c, 0xbf, 0x00, + 0xdd, 0x9d, 0xdd, 0x84, 0x56, 0x41, 0x62, 0x7b, 0x7e, 0x9f, 0x1f, 0xef, 0xdd, 0x7b, 0x4f, 0x06, + 0x8f, 0xfa, 0x94, 0xc7, 0x8c, 0xdb, 0x69, 0xc6, 0x06, 0x61, 0x44, 0xb9, 0x7d, 0xb4, 0xe9, 0x51, + 0x41, 0x36, 0xed, 0x98, 0xf5, 0x69, 0xc4, 0x7b, 0xfe, 0x01, 0x09, 0x93, 0x5e, 0x14, 0x26, 0x87, + 0xdc, 0x4a, 0x33, 0x26, 0x18, 0xbc, 0xad, 0x15, 0x56, 0xa5, 0xb0, 0x4a, 0xc5, 0x4a, 0x27, 0x60, + 0x01, 0x53, 0x1c, 0x5b, 0x46, 0x9a, 0xbe, 0x72, 0x27, 0x60, 0x2c, 0x88, 0xa8, 0xad, 0xbe, 0xbc, + 0x7c, 0x60, 0x93, 0x64, 0x54, 0x42, 0xe8, 0x2a, 0x24, 0xc2, 0x98, 0x72, 0x41, 0xe2, 0xb4, 0xd2, + 0xfa, 0x4c, 0x96, 0xea, 0x69, 0x53, 0xfd, 0xa1, 0x21, 0xfc, 0x69, 0x0e, 0x34, 0xf6, 0x64, 0x6f, + 0xaf, 0xc3, 0xe4, 0x10, 0xde, 0x03, 0xf3, 0x39, 0xa7, 0x59, 0xd7, 0x58, 0x33, 0xd6, 0x1b, 0xce, + 0x8d, 0x71, 0x81, 0x9a, 0x23, 0x12, 0x47, 0x3b, 0x58, 0x66, 0xb1, 0xab, 0x40, 0xf8, 0x16, 0x2c, + 0x92, 0x7e, 0x3f, 0xa3, 0x9c, 0x77, 0xff, 0x5b, 0x33, 0xd6, 0x9b, 0x5b, 0x1d, 0x4b, 0x37, 0x60, + 0x55, 0x0d, 0x58, 0xbb, 0xc9, 0xc8, 0xb9, 0x3b, 0x2e, 0xd0, 0xff, 0x5a, 0x5d, 0xd2, 0xf1, 0xd7, + 0xcf, 0x1b, 0xcd, 0x5d, 0x1d, 0x3f, 0x27, 0x82, 0xb8, 0x95, 0x0f, 0x7c, 0x09, 0xea, 0x69, 0xc6, + 0xd8, 0xa0, 0x3b, 0xa7, 0x0c, 0x4d, 0x6b, 0xc6, 0x6c, 0xac, 0x37, 0x92, 0xe5, 0x74, 0x4e, 0x0b, + 0x54, 0x1b, 0x17, 0xa8, 0xa5, 0xed, 0x95, 0x14, 0xbb, 0xda, 0x02, 0xf6, 0x41, 0x4b, 0x0f, 0xdb, + 0x67, 0xc9, 0x20, 0x0c, 0xba, 0xf3, 0xca, 0xf2, 0xfe, 0x4c, 0x4b, 0xf5, 0xfa, 0x3d, 0xc5, 0x75, + 0x56, 0x4b, 0xe3, 0x9b, 0xda, 0x78, 0xda, 0x07, 0xbb, 0x4d, 0x7f, 0xc2, 0x84, 0x04, 0xb4, 0xfd, + 0x8c, 0x12, 0x11, 0xb2, 0xa4, 0x27, 0xc7, 0xdd, 0xad, 0xab, 0x32, 0x2b, 0xd7, 0x46, 0xb1, 0x5f, + 0xed, 0xc2, 0x59, 0x2b, 0xcd, 0x3b, 0xa5, 0xf9, 0xb4, 0x1c, 0x1f, 0xff, 0x40, 0x86, 0xdb, 0xaa, + 0x72, 0x52, 0xb4, 0xd3, 0xfa, 0x70, 0x82, 0x6a, 0x1f, 0x4f, 0x90, 0xf1, 0xeb, 0x04, 0x19, 0xf8, + 0x19, 0x68, 0x4e, 0x75, 0x2a, 0x37, 0x95, 0x90, 0x98, 0x5e, 0xdf, 0x94, 0xcc, 0x62, 0x57, 0x81, + 0x57, 0x1c, 0xbe, 0x18, 0xa0, 0xae, 0xe6, 0x07, 0x77, 0xc1, 0x62, 0x9a, 0x7b, 0xbd, 0x43, 0x3a, + 0x52, 0xfa, 0x59, 0x1b, 0x84, 0x93, 0x0d, 0x96, 0x74, 0xec, 0x2e, 0xa4, 0xb9, 0xf7, 0x8a, 0x8e, + 0xe0, 0x16, 0x68, 0xf0, 0x30, 0x48, 0x88, 0xc8, 0x33, 0xaa, 0xce, 0xa0, 0xe1, 0x74, 0xc6, 0x05, + 0x5a, 0xd2, 0xf4, 0x4b, 0x08, 0xbb, 0x13, 0x1a, 0x7c, 0x0c, 0x40, 0x1a, 0xc9, 0x89, 0x0a, 0x3a, + 0x14, 0x6a, 0xd5, 0x0d, 0xe7, 0xd6, 0xb8, 0x40, 0xcb, 0x65, 0x8d, 0x4b, 0x0c, 0xbb, 0x0d, 0xf5, + 0xb1, 0x4f, 0x87, 0xe2, 0xca, 0x23, 0xde, 0x83, 0xb6, 0x43, 0xfd, 0x83, 0xed, 0xad, 0xf2, 0x8e, + 0xe0, 0x03, 0x50, 0x3f, 0x22, 0x51, 0x5e, 0x4d, 0x62, 0x69, 0x72, 0x16, 0x2a, 0x8d, 0x5d, 0x0d, + 0xc3, 0x87, 0x60, 0x21, 0xcd, 0xe8, 0x20, 0x1c, 0x96, 0xdd, 0x2e, 0x8f, 0x0b, 0xd4, 0xae, 0xee, + 0x47, 0xe6, 0xe5, 0xdb, 0x54, 0xb0, 0xb3, 0x3a, 0x5d, 0xf1, 0xdb, 0x9f, 0x37, 0x8b, 0xf7, 0x41, + 0xdb, 0x21, 0x9c, 0x3e, 0x79, 0xfa, 0x8f, 0x0d, 0xfc, 0xd5, 0xd5, 0x79, 0x71, 0x7a, 0x6e, 0x1a, + 0x67, 0xe7, 0xa6, 0xf1, 0xf3, 0xdc, 0x34, 0x8e, 0x2f, 0xcc, 0xda, 0xd9, 0x85, 0x59, 0xfb, 0x7e, + 0x61, 0xd6, 0xde, 0x59, 0x41, 0x28, 0x0e, 0x72, 0xcf, 0xf2, 0x59, 0x6c, 0xeb, 0x13, 0xde, 0x88, + 0x88, 0xc7, 0xcb, 0xd8, 0x1e, 0x4e, 0xfe, 0x38, 0x62, 0x94, 0x52, 0xee, 0x2d, 0xa8, 0x15, 0x6e, + 0xff, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x15, 0x97, 0x7f, 0xeb, 0x91, 0x04, 0x00, 0x00, } func (this *ChainLink) Equal(that interface{}) bool { diff --git a/x/profiles/types/models_chain_links_test.go b/x/profiles/types/models_chain_links_test.go index 88e0175da7..4182753237 100644 --- a/x/profiles/types/models_chain_links_test.go +++ b/x/profiles/types/models_chain_links_test.go @@ -2,18 +2,23 @@ package types_test import ( "encoding/hex" + + "github.com/mr-tron/base58" + "testing" "time" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/bech32" "github.com/desmos-labs/desmos/app" - "github.com/desmos-labs/desmos/x/profiles/types" - "github.com/stretchr/testify/require" + + "github.com/desmos-labs/desmos/x/profiles/types" ) func TestChainConfig_Validate(t *testing.T) { @@ -94,49 +99,85 @@ func TestProof_Validate(t *testing.T) { } func TestProof_Verify(t *testing.T) { - privKey := secp256k1.GenPrivKey() - pubKey := privKey.PubKey() + bech32PrivKey := secp256k1.GenPrivKey() + bech32PubKey := bech32PrivKey.PubKey() + bech32Addr, err := sdk.Bech32ifyAddressBytes("cosmos", bech32PubKey.Address()) + require.NoError(t, err) + + base58PrivKeyBz, err := hex.DecodeString("bb98111da675930d32f79451fa8d05f188289699558c17148a5d9c82cdb31d1fe04fb0a0d9e689b436b59eff9676d7f2d788244cc4ccfc5768fe117efbd0f9d3") + require.NoError(t, err) + base58PrivKey := ed25519.PrivKey{Key: base58PrivKeyBz} + base58PubKey := base58PrivKey.PubKey() + base58Addr := base58.Encode(base58PubKey.Bytes()) plainText := "test" - sig, err := privKey.Sign([]byte(plainText)) + + bech32Sig, err := bech32PrivKey.Sign([]byte(plainText)) require.NoError(t, err) + bech32SigHex := hex.EncodeToString(bech32Sig) - sigHex := hex.EncodeToString(sig) + base58Sig, err := base58PrivKey.Sign([]byte(plainText)) + require.NoError(t, err) + base58SigHex := hex.EncodeToString(base58Sig) - invalidAny, err := codectypes.NewAnyWithValue(privKey) + invalidAny, err := codectypes.NewAnyWithValue(bech32PrivKey) require.NoError(t, err) tests := []struct { - name string - proof types.Proof - shouldErr bool + name string + proof types.Proof + addressData types.AddressData + shouldErr bool }{ { - name: "Invalid public key value returns error", - proof: types.Proof{PubKey: invalidAny, Signature: sigHex, PlainText: plainText}, - shouldErr: true, + name: "Invalid public key value returns error", + proof: types.Proof{PubKey: invalidAny, Signature: bech32SigHex, PlainText: plainText}, + addressData: types.NewBech32Address(bech32Addr, "cosmos"), + shouldErr: true, }, { - name: "Wrong plain text returns error", - proof: types.NewProof(pubKey, sigHex, "wrong"), - shouldErr: true, + name: "Wrong plain text returns error", + proof: types.NewProof(bech32PubKey, bech32SigHex, "wrong"), + addressData: types.NewBech32Address(bech32Addr, "cosmos"), + shouldErr: true, }, { - name: "Wrong signature returns error", - proof: types.NewProof(pubKey, "74657874", plainText), - shouldErr: true, + name: "Wrong signature returns error", + proof: types.NewProof(bech32PubKey, "74657874", plainText), + addressData: types.NewBech32Address(bech32Addr, "cosmos"), + shouldErr: true, }, { - name: "Correct proof returns no error", - proof: types.NewProof(pubKey, sigHex, plainText), - shouldErr: false, + name: "Wrong Bech32 address returns error", + proof: types.NewProof(bech32PubKey, bech32SigHex, plainText), + addressData: types.NewBech32Address("cosmos1xcy3els9ua75kdm783c3qu0rfa2eplesldfevn", "cosmos"), + shouldErr: true, + }, + { + name: "Wrong Base58 address returns error", + proof: types.NewProof(base58PubKey, base58SigHex, plainText), + addressData: types.NewBase58Address("HWQ14mk82aRMAad2TdxFHbeqLeUGo5SiBxTXyZyTesJT"), + shouldErr: true, + }, + { + name: "Correct proof with Base58 address returns no error", + proof: types.NewProof(base58PubKey, base58SigHex, plainText), + addressData: types.NewBase58Address(base58Addr), + shouldErr: false, + }, + { + name: "Correct proof with Bech32 address returns no error", + proof: types.NewProof(bech32PubKey, bech32SigHex, plainText), + addressData: types.NewBech32Address(bech32Addr, "cosmos"), + shouldErr: false, }, } + for _, test := range tests { cdc, _ := app.MakeCodecs() test := test t.Run(test.name, func(t *testing.T) { - err := test.proof.Verify(cdc) + err := test.proof.Verify(cdc, test.addressData) if test.shouldErr { require.Error(t, err) } else { @@ -193,9 +234,9 @@ func Test_Bech32AddressValidate(t *testing.T) { } } -func Test_Bech32AddressGetAddress(t *testing.T) { +func Test_Bech32AddressGetValue(t *testing.T) { addr := types.NewBech32Address("cosmos1tdgrkvx2qgjk0uqsmdhm6dcz6wvwh9f8t37x0k", "cosmos") - require.Equal(t, "cosmos1tdgrkvx2qgjk0uqsmdhm6dcz6wvwh9f8t37x0k", addr.GetAddress()) + require.Equal(t, "cosmos1tdgrkvx2qgjk0uqsmdhm6dcz6wvwh9f8t37x0k", addr.GetValue()) } // -------------------------------------------------------------------------------------------------------------------- @@ -235,9 +276,9 @@ func Test_Base58AddressValidate(t *testing.T) { } } -func Test_Base58AddressGetAddress(t *testing.T) { +func Test_Base58AddressGetValue(t *testing.T) { addr := types.NewBase58Address("5AfetAwZzftP8i5JBNatzWeccfXd4KvKq6TRfAvacFaN") - require.Equal(t, "5AfetAwZzftP8i5JBNatzWeccfXd4KvKq6TRfAvacFaN", addr.GetAddress()) + require.Equal(t, "5AfetAwZzftP8i5JBNatzWeccfXd4KvKq6TRfAvacFaN", addr.GetValue()) } // -------------------------------------------------------------------------------------------------------------------- diff --git a/x/profiles/types/models_dtag_requests.pb.go b/x/profiles/types/models_dtag_requests.pb.go index 3cbc342f60..a0b1992d4d 100644 --- a/x/profiles/types/models_dtag_requests.pb.go +++ b/x/profiles/types/models_dtag_requests.pb.go @@ -71,27 +71,6 @@ func (m *DTagTransferRequest) XXX_DiscardUnknown() { var xxx_messageInfo_DTagTransferRequest proto.InternalMessageInfo -func (m *DTagTransferRequest) GetDTagToTrade() string { - if m != nil { - return m.DTagToTrade - } - return "" -} - -func (m *DTagTransferRequest) GetSender() string { - if m != nil { - return m.Sender - } - return "" -} - -func (m *DTagTransferRequest) GetReceiver() string { - if m != nil { - return m.Receiver - } - return "" -} - func init() { proto.RegisterType((*DTagTransferRequest)(nil), "desmos.profiles.v1beta1.DTagTransferRequest") } @@ -101,29 +80,30 @@ func init() { } var fileDescriptor_08f2e5360e821c5e = []byte{ - // 350 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x90, 0xb1, 0x4e, 0xfb, 0x30, - 0x10, 0xc6, 0xeb, 0xff, 0x5f, 0xaa, 0x4a, 0xaa, 0x0a, 0x91, 0x56, 0xa2, 0x74, 0x48, 0x50, 0x16, - 0x60, 0x20, 0xa6, 0xb0, 0x75, 0xac, 0x18, 0x90, 0xd8, 0xa2, 0x4e, 0x2c, 0x91, 0x93, 0x5c, 0xd3, - 0x88, 0x24, 0x0e, 0xb6, 0x5b, 0xd1, 0xb7, 0x60, 0x64, 0xec, 0xe3, 0x74, 0xec, 0xc8, 0x14, 0xa1, - 0x74, 0x61, 0xee, 0x13, 0xa0, 0xd8, 0x2e, 0x08, 0x16, 0xb6, 0xbb, 0xfb, 0x7e, 0xdf, 0xd9, 0xf7, - 0x19, 0xd7, 0x11, 0xf0, 0x8c, 0x72, 0x5c, 0x30, 0x3a, 0x4d, 0x52, 0xe0, 0x78, 0x31, 0x0c, 0x40, - 0x90, 0x21, 0xce, 0x68, 0x04, 0x29, 0xf7, 0x23, 0x41, 0x62, 0x9f, 0xc1, 0xd3, 0x1c, 0xb8, 0xe0, - 0x6e, 0xc1, 0xa8, 0xa0, 0xe6, 0xb1, 0xf2, 0xb8, 0x7b, 0x8f, 0xab, 0x3d, 0x83, 0x5e, 0x4c, 0x63, - 0x2a, 0x19, 0x5c, 0x57, 0x0a, 0x1f, 0x9c, 0xc4, 0x94, 0xc6, 0x29, 0x60, 0xd9, 0x05, 0xf3, 0x29, - 0x26, 0xf9, 0x52, 0x4b, 0xf6, 0x6f, 0x49, 0x24, 0x19, 0x70, 0x41, 0xb2, 0x62, 0xef, 0x0d, 0x69, - 0xfd, 0x94, 0xaf, 0x96, 0xaa, 0x46, 0x4b, 0x57, 0x7f, 0xfc, 0x3c, 0x9c, 0x91, 0x24, 0xf7, 0xd3, - 0x24, 0x7f, 0xd4, 0x0e, 0x67, 0x8d, 0x8c, 0xee, 0xed, 0x84, 0xc4, 0x13, 0x46, 0x72, 0x3e, 0x05, - 0xe6, 0xa9, 0xb3, 0xcc, 0x7b, 0xa3, 0x23, 0xcf, 0x14, 0xd4, 0x17, 0x8c, 0x44, 0xd0, 0x47, 0xa7, - 0xe8, 0xfc, 0x60, 0x7c, 0x56, 0x95, 0x76, 0x5b, 0xf2, 0x74, 0x52, 0x8f, 0x77, 0xa5, 0xdd, 0x5b, - 0x92, 0x2c, 0x1d, 0x39, 0x3f, 0x68, 0xc7, 0x6b, 0xd7, 0xbd, 0x86, 0xcc, 0x0b, 0xa3, 0xc9, 0x21, - 0x8f, 0x80, 0xf5, 0xff, 0xc9, 0x2d, 0x47, 0xbb, 0xd2, 0xee, 0x28, 0x9b, 0x9a, 0x3b, 0x9e, 0x06, - 0x4c, 0x6c, 0xb4, 0x18, 0x84, 0x90, 0x2c, 0x80, 0xf5, 0xff, 0x4b, 0xb8, 0xbb, 0x2b, 0xed, 0x43, - 0x05, 0xef, 0x15, 0xc7, 0xfb, 0x82, 0x46, 0xad, 0xd7, 0x95, 0x8d, 0x3e, 0x56, 0x36, 0x1a, 0xdf, - 0xad, 0x2b, 0x0b, 0x6d, 0x2a, 0x0b, 0xbd, 0x57, 0x16, 0x7a, 0xd9, 0x5a, 0x8d, 0xcd, 0xd6, 0x6a, - 0xbc, 0x6d, 0xad, 0xc6, 0x83, 0x1b, 0x27, 0x62, 0x36, 0x0f, 0xdc, 0x90, 0x66, 0x58, 0x25, 0x74, - 0x99, 0x92, 0x80, 0xeb, 0x1a, 0x3f, 0x7f, 0xe7, 0x25, 0x96, 0x05, 0xf0, 0xa0, 0x29, 0xb3, 0xb9, - 0xf9, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x9a, 0x3c, 0x9d, 0x09, 0x09, 0x02, 0x00, 0x00, + // 354 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x90, 0xb1, 0x4e, 0xc3, 0x30, + 0x10, 0x86, 0x63, 0x90, 0x2a, 0x48, 0xa9, 0x10, 0x69, 0x25, 0x4a, 0x87, 0x18, 0x65, 0x01, 0x06, + 0x62, 0x0a, 0x5b, 0xc7, 0x8a, 0x01, 0x89, 0x2d, 0xea, 0xc4, 0x12, 0x39, 0xc9, 0x35, 0x8d, 0x48, + 0xe2, 0x60, 0xbb, 0x15, 0x7d, 0x03, 0x46, 0x46, 0xc6, 0x3e, 0x0e, 0x03, 0x43, 0x47, 0xa6, 0x08, + 0xa5, 0x0b, 0x73, 0x9f, 0x00, 0x25, 0x4e, 0x41, 0xb0, 0xb0, 0xdd, 0xdd, 0xff, 0xfd, 0x67, 0xdf, + 0xaf, 0x5f, 0x06, 0x20, 0x12, 0x26, 0x48, 0xc6, 0xd9, 0x38, 0x8a, 0x41, 0x90, 0x59, 0xdf, 0x03, + 0x49, 0xfb, 0x24, 0x61, 0x01, 0xc4, 0xc2, 0x0d, 0x24, 0x0d, 0x5d, 0x0e, 0x0f, 0x53, 0x10, 0x52, + 0xd8, 0x19, 0x67, 0x92, 0x19, 0x87, 0xca, 0x63, 0x6f, 0x3c, 0x76, 0xed, 0xe9, 0x75, 0x42, 0x16, + 0xb2, 0x8a, 0x21, 0x65, 0xa5, 0xf0, 0xde, 0x51, 0xc8, 0x58, 0x18, 0x03, 0xa9, 0x3a, 0x6f, 0x3a, + 0x26, 0x34, 0x9d, 0xd7, 0x12, 0xfe, 0x2b, 0xc9, 0x28, 0x01, 0x21, 0x69, 0x92, 0x6d, 0xbc, 0x3e, + 0x2b, 0x9f, 0x72, 0xd5, 0x52, 0xd5, 0xd4, 0xd2, 0xc5, 0x3f, 0x3f, 0xf7, 0x27, 0x34, 0x4a, 0xdd, + 0x38, 0x4a, 0xef, 0x6b, 0x87, 0xf5, 0x86, 0xf4, 0xf6, 0xf5, 0x88, 0x86, 0x23, 0x4e, 0x53, 0x31, + 0x06, 0xee, 0xa8, 0xb3, 0x8c, 0x5b, 0xbd, 0x55, 0x9d, 0x29, 0x99, 0x2b, 0x39, 0x0d, 0xa0, 0x8b, + 0x8e, 0xd1, 0xe9, 0xee, 0xf0, 0xa4, 0xc8, 0x71, 0xb3, 0xe2, 0xd9, 0xa8, 0x1c, 0xaf, 0x73, 0xdc, + 0x99, 0xd3, 0x24, 0x1e, 0x58, 0xbf, 0x68, 0xcb, 0x69, 0x96, 0x7d, 0x0d, 0x19, 0x67, 0x7a, 0x43, + 0x40, 0x1a, 0x00, 0xef, 0x6e, 0x55, 0x5b, 0x0e, 0xd6, 0x39, 0x6e, 0x29, 0x9b, 0x9a, 0x5b, 0x4e, + 0x0d, 0x18, 0x44, 0xdf, 0xe1, 0xe0, 0x43, 0x34, 0x03, 0xde, 0xdd, 0xae, 0xe0, 0xf6, 0x3a, 0xc7, + 0xfb, 0x0a, 0xde, 0x28, 0x96, 0xf3, 0x0d, 0x0d, 0xf6, 0x9e, 0x16, 0x58, 0x7b, 0x59, 0x60, 0xf4, + 0xb9, 0xc0, 0x68, 0x78, 0xf3, 0x5a, 0x98, 0x68, 0x59, 0x98, 0xe8, 0xa3, 0x30, 0xd1, 0xf3, 0xca, + 0xd4, 0x96, 0x2b, 0x53, 0x7b, 0x5f, 0x99, 0xda, 0x9d, 0x1d, 0x46, 0x72, 0x32, 0xf5, 0x6c, 0x9f, + 0x25, 0x44, 0xa5, 0x74, 0x1e, 0x53, 0x4f, 0xd4, 0x35, 0x79, 0xfc, 0xc9, 0x4c, 0xce, 0x33, 0x10, + 0x5e, 0xa3, 0xca, 0xe7, 0xea, 0x2b, 0x00, 0x00, 0xff, 0xff, 0xa0, 0xf6, 0xa2, 0xe6, 0x0d, 0x02, + 0x00, 0x00, } func (this *DTagTransferRequest) Equal(that interface{}) bool { diff --git a/x/profiles/types/models_packets.pb.go b/x/profiles/types/models_packets.pb.go index 4cfd3abb10..6b53652306 100644 --- a/x/profiles/types/models_packets.pb.go +++ b/x/profiles/types/models_packets.pb.go @@ -75,41 +75,6 @@ func (m *LinkChainAccountPacketData) XXX_DiscardUnknown() { var xxx_messageInfo_LinkChainAccountPacketData proto.InternalMessageInfo -func (m *LinkChainAccountPacketData) GetSourceAddress() *types.Any { - if m != nil { - return m.SourceAddress - } - return nil -} - -func (m *LinkChainAccountPacketData) GetSourceProof() Proof { - if m != nil { - return m.SourceProof - } - return Proof{} -} - -func (m *LinkChainAccountPacketData) GetSourceChainConfig() ChainConfig { - if m != nil { - return m.SourceChainConfig - } - return ChainConfig{} -} - -func (m *LinkChainAccountPacketData) GetDestinationAddress() string { - if m != nil { - return m.DestinationAddress - } - return "" -} - -func (m *LinkChainAccountPacketData) GetDestinationProof() Proof { - if m != nil { - return m.DestinationProof - } - return Proof{} -} - // LinkChainAccountPacketAck defines a struct for the packet acknowledgment type LinkChainAccountPacketAck struct { // SourceAddress contains the external address that has been linked properly @@ -150,13 +115,6 @@ func (m *LinkChainAccountPacketAck) XXX_DiscardUnknown() { var xxx_messageInfo_LinkChainAccountPacketAck proto.InternalMessageInfo -func (m *LinkChainAccountPacketAck) GetSourceAddress() string { - if m != nil { - return m.SourceAddress - } - return "" -} - func init() { proto.RegisterType((*LinkChainAccountPacketData)(nil), "desmos.profiles.v1beta1.LinkChainAccountPacketData") proto.RegisterType((*LinkChainAccountPacketAck)(nil), "desmos.profiles.v1beta1.LinkChainAccountPacketAck") @@ -167,36 +125,36 @@ func init() { } var fileDescriptor_923faf54c46abe52 = []byte{ - // 453 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0x41, 0x6e, 0xd3, 0x40, - 0x14, 0x8d, 0xa1, 0x20, 0xd5, 0x01, 0x44, 0x9d, 0x22, 0xd2, 0x20, 0x39, 0xd1, 0x08, 0xa4, 0x2c, - 0xe8, 0x0c, 0x85, 0x1d, 0xbb, 0xb8, 0x2c, 0x58, 0x20, 0x51, 0x65, 0xc9, 0x82, 0x68, 0x3c, 0x9e, - 0xb8, 0xa3, 0xd8, 0xf3, 0x2d, 0xcf, 0x04, 0x35, 0xb7, 0xe0, 0x02, 0xdc, 0x82, 0x43, 0x54, 0xac, - 0xba, 0x64, 0x15, 0xa1, 0xe4, 0x06, 0x3d, 0x01, 0xf2, 0xcc, 0x98, 0x3a, 0x90, 0x88, 0xee, 0xe6, - 0xff, 0xff, 0xde, 0x7f, 0xcf, 0xcf, 0xdf, 0x7f, 0x99, 0x70, 0x95, 0x83, 0x22, 0x45, 0x09, 0x53, - 0x91, 0x71, 0x45, 0xbe, 0x9c, 0xc4, 0x5c, 0xd3, 0x13, 0x92, 0x43, 0xc2, 0x33, 0x35, 0x29, 0x28, - 0x9b, 0x71, 0xad, 0x70, 0x51, 0x82, 0x86, 0xe0, 0xa9, 0x45, 0xe3, 0x1a, 0x8d, 0x1d, 0xba, 0x77, - 0x98, 0x42, 0x0a, 0x06, 0x43, 0xaa, 0x97, 0x85, 0xf7, 0x8e, 0x52, 0x80, 0x34, 0xe3, 0xc4, 0x54, - 0xf1, 0x7c, 0x4a, 0xa8, 0x5c, 0xd4, 0x23, 0x06, 0xd5, 0xa6, 0x89, 0xe5, 0xd8, 0xc2, 0x8d, 0x5e, - 0xfd, 0xc7, 0x12, 0x3b, 0xa7, 0x42, 0x4e, 0x32, 0x21, 0x67, 0x8e, 0x81, 0xbe, 0xed, 0xf9, 0xbd, - 0x0f, 0x42, 0xce, 0x4e, 0xab, 0xc9, 0x88, 0x31, 0x98, 0x4b, 0x7d, 0x66, 0x8c, 0xbf, 0xa3, 0x9a, - 0x06, 0xdc, 0x7f, 0xa4, 0x60, 0x5e, 0x32, 0x3e, 0xa1, 0x49, 0x52, 0x72, 0xa5, 0xba, 0xde, 0xc0, - 0x1b, 0xb6, 0x5f, 0x1f, 0x62, 0xeb, 0x0f, 0xd7, 0xfe, 0xf0, 0x48, 0x2e, 0xa2, 0xe1, 0xf5, 0xb2, - 0xff, 0x64, 0x41, 0xf3, 0xec, 0x2d, 0xda, 0x64, 0xa1, 0x1f, 0xdf, 0x8f, 0xdb, 0x23, 0xfb, 0xae, - 0xf6, 0x8e, 0x1f, 0xda, 0xb9, 0x6b, 0x05, 0x9f, 0xfd, 0x07, 0x8e, 0x50, 0x94, 0x00, 0xd3, 0xee, - 0x1d, 0x23, 0x12, 0xe2, 0x1d, 0x99, 0xe1, 0xb3, 0x0a, 0x15, 0x3d, 0xbb, 0x5c, 0xf6, 0x5b, 0xd7, - 0xcb, 0x7e, 0x67, 0x43, 0xd2, 0x6c, 0x40, 0xe3, 0xb6, 0x2d, 0x0d, 0x32, 0xb8, 0xf0, 0x3b, 0x6e, - 0x6a, 0x13, 0x60, 0x20, 0xa7, 0x22, 0xed, 0xde, 0x35, 0x32, 0xcf, 0x77, 0xca, 0x98, 0x50, 0x4e, - 0x0d, 0x36, 0x42, 0x4e, 0xac, 0xb7, 0x21, 0xd6, 0x5c, 0x87, 0xc6, 0x07, 0xb6, 0xdb, 0xa0, 0x05, - 0x1f, 0xfd, 0x4e, 0xc2, 0x95, 0x16, 0x92, 0x6a, 0x01, 0xf2, 0x4f, 0x8a, 0x7b, 0x03, 0x6f, 0xb8, - 0x1f, 0x85, 0x37, 0xfb, 0xb6, 0x80, 0xd0, 0x38, 0x68, 0x74, 0xeb, 0xa8, 0x72, 0xff, 0xa0, 0x89, - 0xb5, 0x79, 0xdd, 0xbb, 0x55, 0x5e, 0x03, 0xf7, 0x09, 0xdd, 0x7f, 0x25, 0x5d, 0x68, 0x8f, 0x1b, - 0x3d, 0xc3, 0x41, 0x91, 0x7f, 0xb4, 0xfd, 0x3c, 0x46, 0x6c, 0x16, 0xbc, 0xd8, 0x7a, 0x1d, 0xfb, - 0x7f, 0xfd, 0xdd, 0xe8, 0xfd, 0xe5, 0x2a, 0xf4, 0xae, 0x56, 0xa1, 0xf7, 0x6b, 0x15, 0x7a, 0x5f, - 0xd7, 0x61, 0xeb, 0x6a, 0x1d, 0xb6, 0x7e, 0xae, 0xc3, 0xd6, 0x27, 0x9c, 0x0a, 0x7d, 0x3e, 0x8f, - 0x31, 0x83, 0x9c, 0x58, 0xef, 0xc7, 0x19, 0x8d, 0x95, 0x7b, 0x93, 0x8b, 0x9b, 0x43, 0xd6, 0x8b, - 0x82, 0xab, 0xf8, 0xbe, 0x39, 0xb7, 0x37, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x34, 0xda, 0xea, - 0x2d, 0x7b, 0x03, 0x00, 0x00, + // 463 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0x4d, 0x6e, 0xd3, 0x40, + 0x14, 0xb6, 0x21, 0x20, 0xd5, 0x01, 0x44, 0x9d, 0x22, 0xd2, 0x20, 0x39, 0x91, 0x05, 0x52, 0x16, + 0x74, 0x86, 0xc2, 0xae, 0xbb, 0xb8, 0x2c, 0xba, 0x40, 0xa2, 0xca, 0x92, 0x05, 0xd1, 0x78, 0x3c, + 0x71, 0x47, 0xb1, 0xe7, 0x59, 0x9e, 0x09, 0x6a, 0x6e, 0xc0, 0x92, 0x23, 0x20, 0x71, 0x05, 0x0e, + 0x51, 0xb1, 0xea, 0x92, 0x55, 0x84, 0x92, 0x1b, 0xf4, 0x04, 0xc8, 0x33, 0x63, 0xea, 0x40, 0x2a, + 0xba, 0x9b, 0xf7, 0xde, 0xf7, 0xf3, 0xfc, 0xf9, 0x79, 0x2f, 0x13, 0x26, 0x73, 0x90, 0xb8, 0x28, + 0x61, 0xca, 0x33, 0x26, 0xf1, 0xa7, 0xc3, 0x98, 0x29, 0x72, 0x88, 0x73, 0x48, 0x58, 0x26, 0x27, + 0x05, 0xa1, 0x33, 0xa6, 0x24, 0x2a, 0x4a, 0x50, 0xe0, 0x3f, 0x35, 0x68, 0x54, 0xa3, 0x91, 0x45, + 0xf7, 0xf6, 0x52, 0x48, 0x41, 0x63, 0x70, 0xf5, 0x32, 0xf0, 0xde, 0x7e, 0x0a, 0x90, 0x66, 0x0c, + 0xeb, 0x2a, 0x9e, 0x4f, 0x31, 0x11, 0x8b, 0x7a, 0x44, 0xa1, 0x52, 0x9a, 0x18, 0x8e, 0x29, 0xec, + 0xe8, 0xd5, 0x7f, 0x56, 0xa2, 0x67, 0x84, 0x8b, 0x49, 0xc6, 0xc5, 0xcc, 0x32, 0xc2, 0x6f, 0x2d, + 0xaf, 0xf7, 0x8e, 0x8b, 0xd9, 0x71, 0x35, 0x19, 0x51, 0x0a, 0x73, 0xa1, 0x4e, 0xf5, 0xe2, 0x6f, + 0x89, 0x22, 0x3e, 0xf3, 0x1e, 0x49, 0x98, 0x97, 0x94, 0x4d, 0x48, 0x92, 0x94, 0x4c, 0xca, 0xae, + 0x3b, 0x70, 0x87, 0xed, 0xd7, 0x7b, 0xc8, 0xec, 0x87, 0xea, 0xfd, 0xd0, 0x48, 0x2c, 0xa2, 0xe1, + 0xd5, 0xb2, 0xff, 0x64, 0x41, 0xf2, 0xec, 0x28, 0xdc, 0x64, 0x85, 0x3f, 0xbe, 0x1f, 0xb4, 0x47, + 0xe6, 0x5d, 0xe9, 0x8e, 0x1f, 0x9a, 0xb9, 0x6d, 0xf9, 0x1f, 0xbd, 0x07, 0x96, 0x50, 0x94, 0x00, + 0xd3, 0xee, 0x1d, 0x6d, 0x12, 0xa0, 0x1b, 0x32, 0x43, 0xa7, 0x15, 0x2a, 0x7a, 0x76, 0xb1, 0xec, + 0x3b, 0x57, 0xcb, 0x7e, 0x67, 0xc3, 0x52, 0x2b, 0x84, 0xe3, 0xb6, 0x29, 0x35, 0xd2, 0x3f, 0xf7, + 0x3a, 0x76, 0x6a, 0x12, 0xa0, 0x20, 0xa6, 0x3c, 0xed, 0xde, 0xd5, 0x36, 0xcf, 0x6f, 0xb4, 0xd1, + 0xa1, 0x1c, 0x6b, 0x6c, 0x14, 0x5a, 0xb3, 0xde, 0x86, 0x59, 0x53, 0x2e, 0x1c, 0xef, 0x9a, 0x6e, + 0x83, 0xe6, 0xbf, 0xf7, 0x3a, 0x09, 0x93, 0x8a, 0x0b, 0xa2, 0x38, 0x88, 0x3f, 0x29, 0xb6, 0x06, + 0xee, 0x70, 0x27, 0x0a, 0xae, 0xf5, 0xb6, 0x80, 0xc2, 0xb1, 0xdf, 0xe8, 0xd6, 0x51, 0xe5, 0xde, + 0x6e, 0x13, 0x6b, 0xf2, 0xba, 0x77, 0xab, 0xbc, 0x06, 0xf6, 0x13, 0xba, 0xff, 0x5a, 0xda, 0xd0, + 0x1e, 0x37, 0x7a, 0x9a, 0x73, 0xd4, 0xfa, 0xfc, 0xb5, 0xef, 0x84, 0x27, 0xde, 0xfe, 0xf6, 0x23, + 0x19, 0xd1, 0x99, 0xff, 0x62, 0xeb, 0x8d, 0xec, 0xfc, 0xf5, 0x8f, 0x8d, 0x52, 0x74, 0x72, 0xb1, + 0x0a, 0xdc, 0xcb, 0x55, 0xe0, 0xfe, 0x5a, 0x05, 0xee, 0x97, 0x75, 0xe0, 0x5c, 0xae, 0x03, 0xe7, + 0xe7, 0x3a, 0x70, 0x3e, 0xa0, 0x94, 0xab, 0xb3, 0x79, 0x8c, 0x28, 0xe4, 0xd8, 0x7c, 0xc7, 0x41, + 0x46, 0x62, 0x69, 0xdf, 0xf8, 0xfc, 0xfa, 0xa8, 0xd5, 0xa2, 0x60, 0x32, 0xbe, 0xaf, 0x4f, 0xef, + 0xcd, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0x54, 0x7e, 0x0e, 0x6b, 0x87, 0x03, 0x00, 0x00, } func (m *LinkChainAccountPacketData) Marshal() (dAtA []byte, err error) { diff --git a/x/profiles/types/models_params.pb.go b/x/profiles/types/models_params.pb.go index f7957be0c7..e11ababd50 100644 --- a/x/profiles/types/models_params.pb.go +++ b/x/profiles/types/models_params.pb.go @@ -64,20 +64,6 @@ func (m *Params) XXX_DiscardUnknown() { var xxx_messageInfo_Params proto.InternalMessageInfo -func (m *Params) GetNicknameParams() NicknameParams { - if m != nil { - return m.NicknameParams - } - return NicknameParams{} -} - -func (m *Params) GetDTagParams() DTagParams { - if m != nil { - return m.DTagParams - } - return DTagParams{} -} - // NicknameParams defines the parameters related to the profiles nicknames type NicknameParams struct { MinNicknameLength github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=min_nickname_length,json=minNicknameLength,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"min_nickname_length" yaml:"min_nickname_length"` @@ -157,13 +143,6 @@ func (m *DTagParams) XXX_DiscardUnknown() { var xxx_messageInfo_DTagParams proto.InternalMessageInfo -func (m *DTagParams) GetRegEx() string { - if m != nil { - return m.RegEx - } - return "" -} - func init() { proto.RegisterType((*Params)(nil), "desmos.profiles.v1beta1.Params") proto.RegisterType((*NicknameParams)(nil), "desmos.profiles.v1beta1.NicknameParams") @@ -175,38 +154,39 @@ func init() { } var fileDescriptor_a621950d5c07fbad = []byte{ - // 486 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x94, 0x4d, 0x6f, 0xd3, 0x30, - 0x00, 0x86, 0x9b, 0x4c, 0x54, 0xc2, 0x5b, 0x3b, 0x2d, 0x7c, 0x4d, 0x3b, 0x24, 0x93, 0x91, 0xa0, - 0x12, 0x5a, 0xa2, 0xc1, 0x8d, 0x63, 0x34, 0x04, 0x48, 0x03, 0xa1, 0x88, 0x0b, 0x5c, 0x22, 0xa7, - 0x35, 0x99, 0xb5, 0xd8, 0x8e, 0x92, 0x80, 0x3c, 0x89, 0x0b, 0x77, 0x0e, 0xfc, 0x22, 0xce, 0x3b, - 0xee, 0x08, 0x1c, 0x2c, 0x94, 0xfe, 0x83, 0xfe, 0x02, 0xe4, 0x8f, 0xd2, 0x66, 0x65, 0x87, 0xb2, - 0x53, 0x6d, 0xeb, 0xed, 0xfb, 0x3c, 0xb6, 0xeb, 0x82, 0x47, 0x13, 0x5c, 0x53, 0x5e, 0x47, 0x65, - 0xc5, 0x3f, 0x90, 0x02, 0xd7, 0xd1, 0xa7, 0xc3, 0x0c, 0x37, 0xe8, 0x30, 0xa2, 0x7c, 0x82, 0x8b, - 0x3a, 0x2d, 0x51, 0x85, 0x68, 0x1d, 0x96, 0x15, 0x6f, 0xb8, 0x77, 0xcf, 0x84, 0xc3, 0x79, 0x38, - 0xb4, 0xe1, 0xbd, 0xdb, 0x39, 0xcf, 0xb9, 0xce, 0x44, 0x6a, 0x64, 0xe2, 0xf0, 0xa7, 0x0b, 0xfa, - 0x6f, 0xf4, 0xf7, 0xbd, 0x12, 0x6c, 0x33, 0x32, 0x3e, 0x65, 0x88, 0x62, 0x5b, 0xb9, 0xeb, 0xec, - 0x3b, 0xa3, 0xcd, 0xc7, 0x0f, 0xc3, 0x2b, 0x3a, 0xc3, 0xd7, 0x36, 0x6f, 0x1a, 0x62, 0xff, 0x5c, - 0x06, 0xbd, 0x99, 0x0c, 0xee, 0x9e, 0x21, 0x5a, 0x3c, 0x85, 0x97, 0xda, 0x60, 0x32, 0x64, 0x9d, - 0xbc, 0xc7, 0xc0, 0xe6, 0xa4, 0x41, 0xf9, 0x9c, 0xe6, 0x6a, 0xda, 0xfd, 0x2b, 0x69, 0x47, 0x6f, - 0x51, 0x6e, 0x49, 0x23, 0x45, 0x6a, 0x65, 0x00, 0x16, 0x6b, 0x33, 0x19, 0x78, 0x86, 0xbb, 0xd4, - 0x09, 0x13, 0xa0, 0x66, 0x96, 0x47, 0xc1, 0x90, 0x22, 0x91, 0x66, 0x84, 0xa7, 0x05, 0x66, 0x79, - 0x73, 0xb2, 0xbb, 0xb1, 0xef, 0x8c, 0xb6, 0xe2, 0xe7, 0xaa, 0xed, 0x97, 0x0c, 0x1e, 0xe4, 0xa4, - 0x39, 0xf9, 0x98, 0x85, 0x63, 0x4e, 0xa3, 0x31, 0xd7, 0x67, 0x6e, 0x3e, 0x0e, 0xea, 0xc9, 0x69, - 0xd4, 0x9c, 0x95, 0xb8, 0x0e, 0x5f, 0xb2, 0x66, 0x26, 0x83, 0x3b, 0x86, 0xd4, 0x6d, 0x83, 0xc9, - 0x16, 0x45, 0x22, 0x26, 0xfc, 0xd8, 0x4c, 0xbf, 0xba, 0x60, 0xd8, 0x3d, 0x21, 0xef, 0x33, 0xb8, - 0x45, 0x09, 0x4b, 0xff, 0x9e, 0x8c, 0xd5, 0x70, 0xb4, 0xc6, 0xf1, 0xda, 0x1a, 0x7b, 0x56, 0x63, - 0xb5, 0x12, 0x26, 0x3b, 0x94, 0xb0, 0x39, 0xdd, 0x08, 0x69, 0x3a, 0x12, 0x2b, 0x74, 0xf7, 0x9a, - 0xf4, 0xd5, 0x4a, 0x45, 0x47, 0xa2, 0x4b, 0x87, 0xdf, 0x5d, 0xb0, 0x74, 0x5d, 0xde, 0x08, 0xf4, - 0x2b, 0x9c, 0xa7, 0x58, 0xe8, 0xdd, 0xdf, 0x8c, 0x77, 0x66, 0x32, 0x18, 0x98, 0x46, 0xb3, 0x0e, - 0x93, 0x1b, 0x15, 0xce, 0x9f, 0x09, 0xef, 0x8b, 0x03, 0xb6, 0xd5, 0x16, 0xf5, 0xbd, 0x76, 0x9c, - 0xdf, 0xad, 0xe7, 0xdc, 0xca, 0x60, 0xf0, 0x8a, 0x30, 0x25, 0x61, 0x9c, 0x16, 0xbf, 0xd5, 0x4b, - 0xfd, 0x30, 0x19, 0x50, 0xc2, 0x8e, 0x9a, 0x79, 0xd0, 0x38, 0x20, 0xd1, 0x71, 0xd8, 0xf8, 0x6f, - 0x07, 0x24, 0xfe, 0xe9, 0xd0, 0xed, 0x57, 0x0e, 0x48, 0x2c, 0x1c, 0xe2, 0x17, 0xe7, 0xad, 0xef, - 0x5c, 0xb4, 0xbe, 0xf3, 0xbb, 0xf5, 0x9d, 0x6f, 0x53, 0xbf, 0x77, 0x31, 0xf5, 0x7b, 0x3f, 0xa6, - 0x7e, 0xef, 0x7d, 0xb8, 0xc4, 0x36, 0xaf, 0xe7, 0xa0, 0x40, 0x59, 0x6d, 0xc7, 0x91, 0x58, 0xfc, - 0x75, 0x68, 0x8f, 0xac, 0xaf, 0x1f, 0xff, 0x93, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xd4, 0x9b, - 0xdf, 0x79, 0x5a, 0x04, 0x00, 0x00, + // 499 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x94, 0xcf, 0x6e, 0xd3, 0x30, + 0x1c, 0xc7, 0x93, 0x0c, 0x2a, 0xe1, 0xad, 0x9d, 0x16, 0xfe, 0x4d, 0x3b, 0xc4, 0x93, 0x91, 0xa0, + 0x12, 0x5a, 0xa2, 0xc1, 0x6d, 0xc7, 0x68, 0x08, 0x90, 0x06, 0x42, 0x11, 0x17, 0xb8, 0x44, 0x4e, + 0x6b, 0x32, 0x6b, 0xb1, 0x1d, 0x25, 0x01, 0x79, 0x12, 0x17, 0x6e, 0x1c, 0x11, 0x4f, 0xc0, 0xc3, + 0x70, 0xd8, 0x71, 0x47, 0xc4, 0x21, 0xa0, 0xf4, 0x0d, 0xfa, 0x04, 0x28, 0xb6, 0x4b, 0x9b, 0x95, + 0x1d, 0xca, 0x4e, 0x8d, 0xad, 0x6f, 0xbf, 0x9f, 0x4f, 0x7f, 0x6e, 0x0c, 0x1e, 0x8e, 0x49, 0xc9, + 0x44, 0x19, 0xe4, 0x85, 0x78, 0x47, 0x33, 0x52, 0x06, 0x1f, 0xf6, 0x13, 0x52, 0xe1, 0xfd, 0x80, + 0x89, 0x31, 0xc9, 0xca, 0x38, 0xc7, 0x05, 0x66, 0xa5, 0x9f, 0x17, 0xa2, 0x12, 0xee, 0x5d, 0x1d, + 0xf6, 0x67, 0x61, 0xdf, 0x84, 0x77, 0x6e, 0xa5, 0x22, 0x15, 0x2a, 0x13, 0xb4, 0x4f, 0x3a, 0x8e, + 0x7e, 0x39, 0xa0, 0xf7, 0x4a, 0x7d, 0xdf, 0xcd, 0xc1, 0x26, 0xa7, 0xa3, 0x13, 0x8e, 0x19, 0x31, + 0x95, 0xdb, 0xf6, 0xae, 0x3d, 0x5c, 0x7f, 0xf4, 0xc0, 0xbf, 0xa4, 0xd3, 0x7f, 0x69, 0xf2, 0xba, + 0x21, 0xf4, 0xce, 0x6a, 0x68, 0x4d, 0x6b, 0x78, 0xe7, 0x14, 0xb3, 0xec, 0x00, 0x5d, 0x68, 0x43, + 0xd1, 0x80, 0x77, 0xf2, 0x2e, 0x07, 0xeb, 0xe3, 0x0a, 0xa7, 0x33, 0x9a, 0xa3, 0x68, 0xf7, 0x2e, + 0xa5, 0x1d, 0xbe, 0xc6, 0xa9, 0x21, 0x0d, 0x5b, 0x52, 0x53, 0x43, 0x30, 0xdf, 0x9b, 0xd6, 0xd0, + 0xd5, 0xdc, 0x85, 0x4e, 0x14, 0x81, 0x76, 0x65, 0x78, 0x0c, 0x0c, 0x18, 0x96, 0x71, 0x42, 0x45, + 0x9c, 0x11, 0x9e, 0x56, 0xc7, 0xdb, 0x6b, 0xbb, 0xf6, 0x70, 0x23, 0x7c, 0xda, 0xb6, 0xfd, 0xac, + 0xe1, 0xfd, 0x94, 0x56, 0xc7, 0xef, 0x13, 0x7f, 0x24, 0x58, 0x30, 0x12, 0x6a, 0xe6, 0xfa, 0x63, + 0xaf, 0x1c, 0x9f, 0x04, 0xd5, 0x69, 0x4e, 0x4a, 0xff, 0x39, 0xaf, 0xa6, 0x35, 0xbc, 0xad, 0x49, + 0xdd, 0x36, 0x14, 0x6d, 0x30, 0x2c, 0x43, 0x2a, 0x8e, 0xd4, 0xf2, 0xe0, 0xda, 0xe7, 0x6f, 0xd0, + 0x42, 0x5f, 0x1d, 0x30, 0xe8, 0xce, 0xc9, 0xfd, 0x08, 0x6e, 0x32, 0xca, 0xe3, 0xbf, 0xf3, 0x31, + 0x32, 0xb6, 0x92, 0x39, 0x5a, 0x59, 0x66, 0xc7, 0xc8, 0x2c, 0x57, 0xa2, 0x68, 0x8b, 0x51, 0x3e, + 0xa3, 0x6b, 0x2d, 0x45, 0xc7, 0x72, 0x89, 0xee, 0x5c, 0x91, 0xbe, 0x5c, 0xd9, 0xd2, 0xb1, 0xec, + 0xd2, 0xcd, 0x50, 0xbe, 0x3b, 0x60, 0xe1, 0xe8, 0xdc, 0x21, 0xe8, 0x15, 0x24, 0x8d, 0x89, 0x54, + 0x33, 0xb8, 0x11, 0x6e, 0x4d, 0x6b, 0xd8, 0xd7, 0xbd, 0x7a, 0x1f, 0x45, 0xd7, 0x0b, 0x92, 0x3e, + 0x91, 0xee, 0x27, 0x1b, 0x6c, 0xb6, 0x3f, 0x54, 0x9d, 0x71, 0xc7, 0xfc, 0xcd, 0x6a, 0xe6, 0x4d, + 0x0d, 0xfb, 0x2f, 0x28, 0x6f, 0x25, 0xb4, 0xd9, 0xfc, 0x7f, 0x7b, 0xa1, 0x1f, 0x45, 0x7d, 0x46, + 0xf9, 0x61, 0x35, 0x0b, 0x6a, 0x07, 0x2c, 0x3b, 0x0e, 0x6b, 0xff, 0xed, 0x80, 0xe5, 0x3f, 0x1d, + 0xba, 0xfd, 0xad, 0x03, 0x96, 0x73, 0x07, 0x3d, 0xc6, 0xf0, 0xd9, 0x59, 0xe3, 0xd9, 0xe7, 0x8d, + 0x67, 0xff, 0x6e, 0x3c, 0xfb, 0xcb, 0xc4, 0xb3, 0xce, 0x27, 0x9e, 0xf5, 0x63, 0xe2, 0x59, 0x6f, + 0xfd, 0x05, 0x03, 0xfd, 0x3e, 0xed, 0x65, 0x38, 0x29, 0xcd, 0x73, 0x20, 0xe7, 0x97, 0x89, 0xb2, + 0x49, 0x7a, 0xea, 0x3a, 0x78, 0xfc, 0x27, 0x00, 0x00, 0xff, 0xff, 0xeb, 0xa6, 0x80, 0x7e, 0x6c, + 0x04, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { diff --git a/x/profiles/types/models_relationships.go b/x/profiles/types/models_relationships.go index 469e2e4478..562cf5db24 100644 --- a/x/profiles/types/models_relationships.go +++ b/x/profiles/types/models_relationships.go @@ -105,20 +105,6 @@ func RemoveUserBlock(blocks []UserBlock, blocker, blocked, subspace string) ([]U return blocks, false } -// MustMarshalUserBlocks serializes the given blocks using the provided BinaryMarshaler -func MustMarshalUserBlocks(cdc codec.BinaryMarshaler, block []UserBlock) []byte { - wrapped := UserBlocks{Blocks: block} - return cdc.MustMarshalBinaryBare(&wrapped) -} - -// MustUnmarshalUserBlocks deserializes the given byte array as an array of blocks using -// the provided BinaryMarshaler -func MustUnmarshalUserBlocks(cdc codec.BinaryMarshaler, bz []byte) []UserBlock { - var wrapped UserBlocks - cdc.MustUnmarshalBinaryBare(bz, &wrapped) - return wrapped.Blocks -} - // MustUnmarshalUserBlock deserializes the given byte array as a UserBlock using the provided BinaryMarshaler func MustUnmarshalUserBlock(cdc codec.BinaryMarshaler, bz []byte) UserBlock { var block UserBlock diff --git a/x/profiles/types/models_relationships.pb.go b/x/profiles/types/models_relationships.pb.go index 705c8f8d9d..fa65d2fb2e 100644 --- a/x/profiles/types/models_relationships.pb.go +++ b/x/profiles/types/models_relationships.pb.go @@ -163,55 +163,9 @@ func (m *UserBlock) GetSubspace() string { return "" } -// UserBlocks wraps a list of UserBlock objects -type UserBlocks struct { - Blocks []UserBlock `protobuf:"bytes,1,rep,name=blocks,proto3" json:"blocks"` -} - -func (m *UserBlocks) Reset() { *m = UserBlocks{} } -func (m *UserBlocks) String() string { return proto.CompactTextString(m) } -func (*UserBlocks) ProtoMessage() {} -func (*UserBlocks) Descriptor() ([]byte, []int) { - return fileDescriptor_47c7d48489f1a7d0, []int{2} -} -func (m *UserBlocks) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *UserBlocks) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_UserBlocks.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *UserBlocks) XXX_Merge(src proto.Message) { - xxx_messageInfo_UserBlocks.Merge(m, src) -} -func (m *UserBlocks) XXX_Size() int { - return m.Size() -} -func (m *UserBlocks) XXX_DiscardUnknown() { - xxx_messageInfo_UserBlocks.DiscardUnknown(m) -} - -var xxx_messageInfo_UserBlocks proto.InternalMessageInfo - -func (m *UserBlocks) GetBlocks() []UserBlock { - if m != nil { - return m.Blocks - } - return nil -} - func init() { proto.RegisterType((*Relationship)(nil), "desmos.profiles.v1beta1.Relationship") proto.RegisterType((*UserBlock)(nil), "desmos.profiles.v1beta1.UserBlock") - proto.RegisterType((*UserBlocks)(nil), "desmos.profiles.v1beta1.UserBlocks") } func init() { @@ -219,34 +173,32 @@ func init() { } var fileDescriptor_47c7d48489f1a7d0 = []byte{ - // 426 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0x3d, 0x8f, 0xd3, 0x30, - 0x1c, 0xc6, 0x63, 0xee, 0x54, 0xae, 0xe6, 0x3d, 0x9c, 0x44, 0xb8, 0x21, 0x3e, 0x79, 0x3a, 0x24, - 0x88, 0xb9, 0xb2, 0x75, 0x42, 0x99, 0x98, 0x18, 0x22, 0xb1, 0xb0, 0x54, 0x4e, 0xe2, 0xa6, 0x56, - 0x9d, 0x38, 0xb2, 0x5d, 0x44, 0xbf, 0x05, 0x23, 0x63, 0x77, 0xbe, 0x48, 0x17, 0xa4, 0x8e, 0x4c, - 0x11, 0x6a, 0x17, 0xe6, 0x7c, 0x02, 0x94, 0xc4, 0x49, 0x79, 0x15, 0x62, 0xfb, 0xdb, 0xcf, 0xef, - 0xb1, 0x9f, 0x47, 0xfa, 0xc3, 0x49, 0xca, 0x74, 0x2e, 0x35, 0x29, 0x95, 0x9c, 0x73, 0xc1, 0x34, - 0x79, 0x77, 0x1d, 0x33, 0x43, 0xaf, 0x49, 0x2e, 0x53, 0x26, 0xf4, 0x4c, 0x31, 0x41, 0x0d, 0x97, - 0x85, 0x5e, 0xf0, 0x52, 0x07, 0xa5, 0x92, 0x46, 0xba, 0x8f, 0x3a, 0x4f, 0xd0, 0x7b, 0x02, 0xeb, - 0xb9, 0x38, 0xcf, 0x64, 0x26, 0x5b, 0x86, 0x34, 0x53, 0x87, 0x5f, 0x3c, 0xce, 0xa4, 0xcc, 0x04, - 0x23, 0xed, 0x29, 0x5e, 0xcd, 0x09, 0x2d, 0xd6, 0x56, 0x42, 0xbf, 0x4a, 0x86, 0xe7, 0x4c, 0x1b, - 0x9a, 0x97, 0xbd, 0x37, 0x91, 0xcd, 0x57, 0xb3, 0xee, 0xd1, 0xee, 0x60, 0xa5, 0xe7, 0xff, 0x48, - 0x9e, 0x2c, 0x28, 0x2f, 0x66, 0x82, 0x17, 0x4b, 0xeb, 0xc0, 0x9f, 0x00, 0xbc, 0x1d, 0xfd, 0xd0, - 0xc7, 0x7d, 0x0a, 0x6f, 0x26, 0x8a, 0x51, 0x23, 0x95, 0x07, 0x2e, 0xc1, 0xd5, 0x38, 0x74, 0xeb, - 0x0a, 0xdd, 0x5d, 0xd3, 0x5c, 0x4c, 0xb1, 0x15, 0x70, 0xd4, 0x23, 0xee, 0x04, 0x8e, 0x15, 0x4b, - 0x78, 0xc9, 0x59, 0x61, 0xbc, 0x1b, 0x2d, 0x7f, 0x5e, 0x57, 0xe8, 0x7e, 0xc7, 0x0f, 0x12, 0x8e, - 0x8e, 0x98, 0x4b, 0xe0, 0x99, 0x5e, 0xc5, 0xba, 0xa4, 0x09, 0xf3, 0x4e, 0x5a, 0xcb, 0xc3, 0xba, - 0x42, 0xf7, 0x3a, 0x4b, 0xaf, 0xe0, 0x68, 0x80, 0xa6, 0x67, 0x1f, 0x37, 0x08, 0x7c, 0xdb, 0x20, - 0x80, 0x3f, 0x03, 0x38, 0x7e, 0xa3, 0x99, 0x0a, 0x85, 0x4c, 0x96, 0x4d, 0xd4, 0xb8, 0x19, 0xd8, - 0x1f, 0xa2, 0x5a, 0x01, 0x47, 0x3d, 0x72, 0xa4, 0x53, 0x1b, 0xf4, 0x37, 0x3a, 0x1d, 0xe8, 0xd4, - 0x7d, 0x02, 0x47, 0x8a, 0x51, 0x2d, 0x0b, 0x1b, 0xf1, 0x41, 0x5d, 0xa1, 0x3b, 0x7d, 0xab, 0xe6, - 0x1e, 0x47, 0x16, 0xf8, 0xa9, 0xcf, 0xe9, 0xff, 0xf5, 0x79, 0x0d, 0xe1, 0x50, 0x47, 0xbb, 0x2f, - 0xe1, 0xa8, 0xfd, 0x5e, 0x7b, 0xe0, 0xf2, 0xe4, 0xea, 0xd6, 0x04, 0x07, 0x7f, 0x59, 0xaa, 0x60, - 0x30, 0x85, 0xa7, 0xdb, 0x0a, 0x39, 0x91, 0xf5, 0x85, 0xaf, 0xb6, 0x7b, 0x1f, 0xec, 0xf6, 0x3e, - 0xf8, 0xba, 0xf7, 0xc1, 0x87, 0x83, 0xef, 0xec, 0x0e, 0xbe, 0xf3, 0xe5, 0xe0, 0x3b, 0x6f, 0x83, - 0x8c, 0x9b, 0xc5, 0x2a, 0x0e, 0x12, 0x99, 0x93, 0xee, 0xd5, 0x67, 0x82, 0xc6, 0xda, 0xce, 0xe4, - 0xfd, 0x71, 0x65, 0xcc, 0xba, 0x64, 0x3a, 0x1e, 0xb5, 0xeb, 0xf1, 0xe2, 0x7b, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x4b, 0x44, 0x7c, 0x8e, 0x0c, 0x03, 0x00, 0x00, + // 393 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x91, 0xbf, 0x4e, 0xeb, 0x30, + 0x18, 0xc5, 0xeb, 0x7b, 0xaf, 0x7a, 0xdb, 0xe8, 0x5e, 0xfe, 0x84, 0x4a, 0x94, 0x0e, 0x09, 0xf2, + 0x04, 0x12, 0xc4, 0xb4, 0x6c, 0x1d, 0x3b, 0x31, 0x47, 0x62, 0x61, 0xa9, 0x9c, 0xc4, 0x4d, 0xad, + 0x3a, 0x71, 0x64, 0xbb, 0x88, 0xbe, 0x05, 0x23, 0x63, 0x77, 0x5e, 0x84, 0x05, 0xa9, 0x23, 0x53, + 0x84, 0xda, 0x85, 0x39, 0x4f, 0x80, 0x92, 0x38, 0x2d, 0xff, 0x24, 0xc4, 0xf6, 0x7d, 0x39, 0xbf, + 0x13, 0x9f, 0xa3, 0xcf, 0xe8, 0x05, 0x44, 0x46, 0x5c, 0xa2, 0x44, 0xf0, 0x11, 0x65, 0x44, 0xa2, + 0xeb, 0xae, 0x47, 0x14, 0xee, 0xa2, 0x88, 0x07, 0x84, 0xc9, 0xa1, 0x20, 0x0c, 0x2b, 0xca, 0x63, + 0x39, 0xa6, 0x89, 0x74, 0x12, 0xc1, 0x15, 0x37, 0xf7, 0x4b, 0x8f, 0x53, 0x79, 0x1c, 0xed, 0xe9, + 0xb4, 0x42, 0x1e, 0xf2, 0x82, 0x41, 0xf9, 0x54, 0xe2, 0x9d, 0x83, 0x90, 0xf3, 0x90, 0x11, 0x54, + 0x6c, 0xde, 0x74, 0x84, 0x70, 0x3c, 0xd3, 0x92, 0xfd, 0x51, 0x52, 0x34, 0x22, 0x52, 0xe1, 0x28, + 0xa9, 0xbc, 0x3e, 0xcf, 0x9f, 0x1a, 0x96, 0x3f, 0x2d, 0x17, 0x2d, 0x9d, 0x7d, 0x93, 0xdc, 0x1f, + 0x63, 0x1a, 0x0f, 0x19, 0x8d, 0x27, 0xda, 0x01, 0xef, 0x81, 0xf1, 0xcf, 0x7d, 0xd3, 0xc7, 0x3c, + 0x31, 0xfe, 0xfa, 0x82, 0x60, 0xc5, 0x45, 0x1b, 0x1c, 0x82, 0xa3, 0xe6, 0xc0, 0xcc, 0x52, 0x7b, + 0x6b, 0x86, 0x23, 0xd6, 0x87, 0x5a, 0x80, 0x6e, 0x85, 0x98, 0x3d, 0xa3, 0x29, 0x88, 0x4f, 0x13, + 0x4a, 0x62, 0xd5, 0xfe, 0x55, 0xf0, 0xad, 0x2c, 0xb5, 0x77, 0x4a, 0x7e, 0x2d, 0x41, 0x77, 0x83, + 0x99, 0xc8, 0x68, 0xc8, 0xa9, 0x27, 0x13, 0xec, 0x93, 0xf6, 0xef, 0xc2, 0xb2, 0x97, 0xa5, 0xf6, + 0x76, 0x69, 0xa9, 0x14, 0xe8, 0xae, 0xa1, 0x7e, 0xe3, 0x6e, 0x6e, 0x83, 0x97, 0xb9, 0x0d, 0xe0, + 0x23, 0x30, 0x9a, 0x97, 0x92, 0x88, 0x01, 0xe3, 0xfe, 0x24, 0x8f, 0xea, 0xe5, 0x03, 0xf9, 0x22, + 0xaa, 0x16, 0xa0, 0x5b, 0x21, 0x1b, 0x3a, 0xd0, 0x41, 0x3f, 0xd1, 0xc1, 0x9a, 0x0e, 0xcc, 0x63, + 0xa3, 0x2e, 0x08, 0x96, 0x3c, 0xd6, 0x11, 0x77, 0xb3, 0xd4, 0xfe, 0x5f, 0xb5, 0xca, 0xbf, 0x43, + 0x57, 0x03, 0xef, 0xfa, 0xfc, 0xf9, 0x51, 0x9f, 0xc1, 0xc5, 0xc3, 0xd2, 0x02, 0x8b, 0xa5, 0x05, + 0x9e, 0x97, 0x16, 0xb8, 0x5d, 0x59, 0xb5, 0xc5, 0xca, 0xaa, 0x3d, 0xad, 0xac, 0xda, 0x95, 0x13, + 0x52, 0x35, 0x9e, 0x7a, 0x8e, 0xcf, 0x23, 0x54, 0x1e, 0xf5, 0x94, 0x61, 0x4f, 0xea, 0x19, 0xdd, + 0x6c, 0x4e, 0xac, 0x66, 0x09, 0x91, 0x5e, 0xbd, 0x38, 0xe7, 0xf9, 0x6b, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x81, 0x0d, 0xce, 0x57, 0xbc, 0x02, 0x00, 0x00, } func (this *Relationship) Equal(that interface{}) bool { @@ -407,43 +359,6 @@ func (m *UserBlock) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *UserBlocks) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *UserBlocks) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *UserBlocks) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Blocks) > 0 { - for iNdEx := len(m.Blocks) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Blocks[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintModelsRelationships(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - } - return len(dAtA) - i, nil -} - func encodeVarintModelsRelationships(dAtA []byte, offset int, v uint64) int { offset -= sovModelsRelationships(v) base := offset @@ -501,21 +416,6 @@ func (m *UserBlock) Size() (n int) { return n } -func (m *UserBlocks) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Blocks) > 0 { - for _, e := range m.Blocks { - l = e.Size() - n += 1 + l + sovModelsRelationships(uint64(l)) - } - } - return n -} - func sovModelsRelationships(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -846,90 +746,6 @@ func (m *UserBlock) Unmarshal(dAtA []byte) error { } return nil } -func (m *UserBlocks) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowModelsRelationships - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: UserBlocks: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: UserBlocks: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Blocks", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowModelsRelationships - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthModelsRelationships - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthModelsRelationships - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Blocks = append(m.Blocks, UserBlock{}) - if err := m.Blocks[len(m.Blocks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipModelsRelationships(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthModelsRelationships - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func skipModelsRelationships(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/profiles/types/models_relationships_test.go b/x/profiles/types/models_relationships_test.go index f3ab6f916d..edf50b55bf 100644 --- a/x/profiles/types/models_relationships_test.go +++ b/x/profiles/types/models_relationships_test.go @@ -186,18 +186,6 @@ func TestRemoveUserBlock(t *testing.T) { } } -func TestUserBlocksMarshaling(t *testing.T) { - cdc, _ := app.MakeCodecs() - blocks := []types.UserBlock{ - types.NewUserBlock("blocker", "blocked_1", "reason", "subspace"), - types.NewUserBlock("blocker", "blocked_2", "reason", "subspace"), - types.NewUserBlock("blocker", "blocked_3", "reason", "subspace"), - } - marshaled := types.MustMarshalUserBlocks(cdc, blocks) - unmarshalled := types.MustUnmarshalUserBlocks(cdc, marshaled) - require.Equal(t, blocks, unmarshalled) -} - func TestUserBlock_Validate(t *testing.T) { tests := []struct { name string diff --git a/x/profiles/types/query_dtag_requests.pb.go b/x/profiles/types/query_dtag_requests.pb.go index 02e3bfa2ea..0ec94a7fa2 100644 --- a/x/profiles/types/query_dtag_requests.pb.go +++ b/x/profiles/types/query_dtag_requests.pb.go @@ -27,10 +27,11 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -// QueryIncomingDTagTransferRequests is the request type for the +// QueryIncomingDTagTransferRequestsRequest is the request type for the // Query/IncomingDTagTransferRequests RPC endpoint type QueryIncomingDTagTransferRequestsRequest struct { - // Receiver represents the address of the user to which query the incoming requests for + // Receiver represents the address of the user to which query the incoming + // requests for Receiver string `protobuf:"bytes,1,opt,name=receiver,proto3" json:"receiver,omitempty"` // Pagination defines an optional pagination for the request Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` diff --git a/x/staging/posts/keeper/common_test.go b/x/staging/posts/keeper/common_test.go index e7d97e5bf2..5624654603 100644 --- a/x/staging/posts/keeper/common_test.go +++ b/x/staging/posts/keeper/common_test.go @@ -4,6 +4,9 @@ import ( "testing" "time" + "github.com/cosmos/cosmos-sdk/crypto/hd" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" @@ -48,6 +51,7 @@ type KeeperTestSuite struct { ctx sdk.Context k keeper.Keeper storeKey sdk.StoreKey + ak authkeeper.AccountKeeper rk profileskeeper.Keeper sk subspaceskeeper.Keeper @@ -57,6 +61,13 @@ type KeeperTestSuite struct { testData TestData } +// TestProfile represents a test profile +type TestProfile struct { + *profilestypes.Profile + + privKey cryptotypes.PrivKey +} + type TestData struct { postID string postOwner string @@ -66,13 +77,14 @@ type TestData struct { answers types.PollAnswers registeredReaction types.RegisteredReaction post types.Post + profile TestProfile subspace subspacetypes.Subspace otherSubspace subspacetypes.Subspace } func (suite *KeeperTestSuite) SetupTest() { // Define the store keys - keys := sdk.NewMemoryStoreKeys(types.StoreKey, paramstypes.StoreKey, profilestypes.StoreKey, subspacetypes.StoreKey, + keys := sdk.NewKVStoreKeys(types.StoreKey, authtypes.StoreKey, paramstypes.StoreKey, profilestypes.StoreKey, subspacetypes.StoreKey, ibchost.StoreKey, capabilitytypes.StoreKey) tKeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) @@ -105,53 +117,57 @@ func (suite *KeeperTestSuite) SetupTest() { ) suite.cdc, suite.legacyAminoCdc = app.MakeCodecs() - pk := paramskeeper.NewKeeper( + paramsKeeper := paramskeeper.NewKeeper( suite.cdc, suite.legacyAminoCdc, keys[paramstypes.StoreKey], tKeys[paramstypes.TStoreKey], ) - ak := authkeeper.NewAccountKeeper( + suite.ak = authkeeper.NewAccountKeeper( suite.cdc, keys[authtypes.StoreKey], - pk.Subspace(authtypes.ModuleName), + paramsKeeper.Subspace(authtypes.ModuleName), authtypes.ProtoBaseAccount, app.GetMaccPerms(), ) - capabilityKeeper := capabilitykeeper.NewKeeper(suite.cdc, keys[capabilitytypes.StoreKey], memKeys[capabilitytypes.MemStoreKey]) + capabilityKeeper := capabilitykeeper.NewKeeper( + suite.cdc, + keys[capabilitytypes.StoreKey], + memKeys[capabilitytypes.MemStoreKey], + ) ScopedProfilesKeeper := capabilityKeeper.ScopeToModule(types.ModuleName) - scopedIBCKeeper := capabilityKeeper.ScopeToModule(ibchost.ModuleName) + IBCKeeper := ibckeeper.NewKeeper( suite.cdc, keys[ibchost.StoreKey], - pk.Subspace(ibchost.ModuleName), + paramsKeeper.Subspace(ibchost.ModuleName), suite.stakingKeeper, scopedIBCKeeper, ) + suite.sk = subspaceskeeper.NewKeeper( + keys[subspacetypes.StoreKey], + suite.cdc, + ) + suite.rk = profileskeeper.NewKeeper( suite.cdc, - suite.storeKey, - pk.Subspace(profilestypes.DefaultParamsSpace), - ak, + keys[profilestypes.StoreKey], + paramsKeeper.Subspace(profilestypes.DefaultParamsSpace), + suite.ak, IBCKeeper.ChannelKeeper, &IBCKeeper.PortKeeper, ScopedProfilesKeeper, ) - suite.sk = subspaceskeeper.NewKeeper( - suite.storeKey, - suite.cdc, - ) - suite.k = keeper.NewKeeper( suite.cdc, - keys[types.StoreKey], - pk.Subspace(types.DefaultParamSpace), + suite.storeKey, + paramsKeeper.Subspace(types.DefaultParamSpace), suite.rk, suite.sk, ) @@ -214,4 +230,41 @@ func (suite *KeeperTestSuite) SetupTest() { "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e", ) + suite.initProfile() +} + +func (suite *KeeperTestSuite) initProfile() { + mnemonic := "ugly like hockey joy digital glow learn remove pet promote screen twenty phone beach aspect mechanic gate piano antenna island loyal possible acoustic jewel" + derivedPrivKey, err := hd.Secp256k1.Derive()(mnemonic, "", sdk.FullFundraiserPath) + suite.Require().NoError(err) + + privKey := hd.Secp256k1.Generate()(derivedPrivKey) + + // Create the base account and set inside the auth keeper. + // This is done in order to make sure that when we try to create a profile using the above address, the profile + // can be created properly. Not storing the base account would end up in the following error since it's null: + // "the given account cannot be serialized using Protobuf" + baseAcc := authtypes.NewBaseAccount(sdk.AccAddress(privKey.PubKey().Address()), privKey.PubKey(), 0, 0) + suite.ak.SetAccount(suite.ctx, baseAcc) + + profile, err := profilestypes.NewProfile( + "dtag", + "test-user", + "biography", + profilestypes.NewPictures( + "https://shorturl.at/adnX3", + "https://shorturl.at/cgpyF", + ), + time.Date(2019, 1, 1, 00, 00, 00, 000, time.UTC), + baseAcc, + ) + suite.Require().NoError(err) + + suite.testData.profile = TestProfile{ + Profile: profile, + privKey: privKey, + } + + err = suite.rk.StoreProfile(suite.ctx, profile) + suite.Require().NoError(err) } diff --git a/x/staging/posts/keeper/msgs_server_test.go b/x/staging/posts/keeper/msgs_server_test.go index 73edf60e6c..1a33d72c41 100644 --- a/x/staging/posts/keeper/msgs_server_test.go +++ b/x/staging/posts/keeper/msgs_server_test.go @@ -166,7 +166,7 @@ func (suite *KeeperTestSuite) TestMsgServer_CreatePost() { name: "Post tag blocked the post creator", storedUserBlocks: []profilestypes.UserBlock{ profilestypes.NewUserBlock( - "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns", + suite.testData.profile.GetAddress().String(), suite.testData.post.Creator, "test", suite.testData.post.Subspace, @@ -183,7 +183,7 @@ func (suite *KeeperTestSuite) TestMsgServer_CreatePost() { types.NewAttachment( "http://uri.com", "text/plain", - []string{"cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns"}, + []string{suite.testData.profile.GetAddress().String()}, ), ), suite.testData.post.PollData, @@ -312,7 +312,7 @@ func (suite *KeeperTestSuite) TestMsgServer_EditPost() { name: "Blocked creator from tags", storedUserBlocks: []profilestypes.UserBlock{ profilestypes.NewUserBlock( - "cosmos1z427v6xdc8jgn5yznfzhwuvetpzzcnusut3z63", + suite.testData.profile.GetAddress().String(), suite.testData.post.Creator, "test", suite.testData.post.Subspace, @@ -328,7 +328,7 @@ func (suite *KeeperTestSuite) TestMsgServer_EditPost() { types.NewAttachment( "https://edited.com", "text/plain", - []string{"cosmos1z427v6xdc8jgn5yznfzhwuvetpzzcnusut3z63"}, + []string{suite.testData.profile.GetAddress().String()}, ), ), nil,