Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: reverse search links #793

Merged
merged 25 commits into from
Mar 28, 2022
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c2d9106
started working on the reverse search for app links
RiccardoM Mar 21, 2022
45ed167
fix: fixed how chain links and app links are deleted
RiccardoM Mar 21, 2022
9cdd3b0
chore: added changeset entry
RiccardoM Mar 21, 2022
ff758d7
updated how DTag transfer requests are deleted
RiccardoM Mar 21, 2022
f65bcfa
removed now unused method
RiccardoM Mar 21, 2022
9afd731
Merge branch 'riccardo/app-chain-links-fixes' into riccardo/reverse-s…
RiccardoM Mar 21, 2022
982826c
removed now unused method
RiccardoM Mar 21, 2022
a3a26f6
added the migration
RiccardoM Mar 21, 2022
90549f7
chore: renamed the v1beta1 store package to v5
RiccardoM Mar 21, 2022
a1f6d38
Merge branch 'riccardo/rename-legacy-package' into riccardo/reverse-s…
RiccardoM Mar 21, 2022
76e26d8
run make format
RiccardoM Mar 21, 2022
2a362e4
Merge branch 'master' of github.com:desmos-labs/desmos into riccardo/…
RiccardoM Mar 22, 2022
ab71a63
chore: updated Proto files
RiccardoM Mar 22, 2022
c643093
feat: added chain link owners keys
RiccardoM Mar 22, 2022
4bc9c28
chore: updated module version
RiccardoM Mar 22, 2022
996e2f8
started working on chain links rever search querier
RiccardoM Mar 22, 2022
6ea38bc
added gRPC tests
RiccardoM Mar 23, 2022
6fea2ff
Updated Swagger definition
RiccardoM Mar 23, 2022
a55649a
Merge branch 'master' of github.com:desmos-labs/desmos into riccardo/…
RiccardoM Mar 23, 2022
63fedef
added changeset entry
RiccardoM Mar 23, 2022
9c48b8d
Merge remote-tracking branch 'origin/riccardo/reverse-search-links' i…
RiccardoM Mar 23, 2022
a51b8b9
added store tests
RiccardoM Mar 25, 2022
ce789c3
updated how chain and app links owners are stored and added tests
RiccardoM Mar 28, 2022
3ee9064
updated test names and added missing tests
RiccardoM Mar 28, 2022
6fd74df
Merge branch 'master' into riccardo/reverse-search-links
mergify[bot] Mar 28, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type: feat
module: x/profiles
pull_request: 793
description: Added the ability to reverse search application links and chain links
backward_compatible: true
date: 2022-03-23T14:27:24.394374729Z
1,476 changes: 1,127 additions & 349 deletions client/docs/swagger-ui/swagger.yaml

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions proto/desmos/profiles/v2/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ service Query {
option (google.api.http).get = "/desmos/profiles/v2/chain-links";
}

// ChainLinkOwners queries for the owners of chain links, optionally searching
// for a specific chain name and external address
rpc ChainLinkOwners(QueryChainLinkOwnersRequest)
returns (QueryChainLinkOwnersResponse) {
option (google.api.http).get = "/desmos/profiles/v2/chain-links/owners";
}

// ApplicationLinks queries the applications links associated to the given
// user, if provided. Otherwise, it queries all the application links stored.
rpc ApplicationLinks(QueryApplicationLinksRequest)
Expand All @@ -51,6 +58,13 @@ service Query {
"/desmos/profiles/v2/app-links/clients/{client_id}";
}

// ApplicationLinkOwners queries for the owners of applications links,
// optionally searching for a specific application and username.
rpc ApplicationLinkOwners(QueryApplicationLinkOwnersRequest)
returns (QueryApplicationLinkOwnersResponse) {
option (google.api.http).get = "/desmos/profiles/v2/app-links/owners";
}

// Params queries the profiles module params
rpc Params(QueryParamsRequest) returns (QueryParamsResponse) {
option (google.api.http).get = "/desmos/profiles/v2/params";
Expand Down
34 changes: 34 additions & 0 deletions proto/desmos/profiles/v2/query_app_links.proto
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,38 @@ message QueryApplicationLinkByClientIDRequest {
// request allowing to get an application link using a client id
message QueryApplicationLinkByClientIDResponse {
ApplicationLink link = 1 [ (gogoproto.nullable) = false ];
}

// QueryApplicationLinkOwnersRequest contains the data of the request that can
// be used to get application link owners
message QueryApplicationLinkOwnersRequest {
// (Optional) Application name to search link owners of. If not specified, all
// links stored will be searched instead.
string application = 1;

// (Optional) Username to search for. This will only be used if the
// application is specified as well
string username = 2;

// Pagination defines an optional pagination for the request
cosmos.base.query.v1beta1.PageRequest pagination = 3;
}

// QueryApplicationLinkOwnersResponse contains the data returned by the request
// allowing to get application link owners.
message QueryApplicationLinkOwnersResponse {
// ApplicationLinkOwnerDetails contains the details of a single application
// link owner
message ApplicationLinkOwnerDetails {
string user = 1;
string application = 2;
string username = 3;
}

// Addresses of the application links owners
repeated ApplicationLinkOwnerDetails owners = 1
[ (gogoproto.nullable) = false ];

// Pagination defines the pagination response
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
32 changes: 32 additions & 0 deletions proto/desmos/profiles/v2/query_chain_links.proto
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,35 @@ message QueryChainLinksResponse {
// Pagination defines the pagination response
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

// QueryChainLinkOwnersRequest contains the data of the request that can
// be used to get chain link owners
message QueryChainLinkOwnersRequest {
// (Optional) Chain name to search link owners of. If not specified, all
// links stored will be searched instead.
string chain_name = 1;

// (Optional) External address to search for. This will only be used if the
// chain name is specified as well
string target = 2;

// Pagination defines an optional pagination for the request
cosmos.base.query.v1beta1.PageRequest pagination = 3;
}

// QueryChainLinkOwnersResponse contains the data returned by the request
// allowing to get chain link owners.
message QueryChainLinkOwnersResponse {
// ChainLinkOwnerDetails contains the details of a single chain link owner
message ChainLinkOwnerDetails {
string user = 1;
string chain_name = 2;
string target = 3;
}

// Addresses of the chain links owners
repeated ChainLinkOwnerDetails owners = 1 [ (gogoproto.nullable) = false ];

// Pagination defines the pagination response
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
11 changes: 11 additions & 0 deletions testutil/profiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"time"

"github.com/cosmos/cosmos-sdk/codec"

codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -48,6 +50,15 @@ func PubKeyFromBech32(pubKey string) cryptotypes.PubKey {
return publicKey
}

func PubKeyFromJSON(cdc codec.Codec, pubKey string) cryptotypes.PubKey {
var publicKey cryptotypes.PubKey
err := cdc.UnmarshalInterfaceJSON([]byte(pubKey), &publicKey)
if err != nil {
panic(err)
}
return publicKey
}

func ProfileFromAddr(address string) *types.Profile {
profile, err := types.NewProfile(
fmt.Sprintf("%s-dtag", address),
Expand Down
75 changes: 75 additions & 0 deletions x/profiles/keeper/grpc_query.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package keeper

import (
"bytes"
"context"
"strings"

Expand Down Expand Up @@ -122,6 +123,43 @@ func (k Keeper) ChainLinks(ctx context.Context, request *types.QueryChainLinksRe
return &types.QueryChainLinksResponse{Links: links, Pagination: pageRes}, nil
}

// ChainLinkOwners implements the Query/ChainLinkOwners gRPC method
func (k Keeper) ChainLinkOwners(ctx context.Context, request *types.QueryChainLinkOwnersRequest) (*types.QueryChainLinkOwnersResponse, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
store := sdkCtx.KVStore(k.storeKey)

ownersPrefix := types.ChainLinkChainPrefix
switch {
case request.ChainName != "" && request.Target != "":
ownersPrefix = types.ChainLinkChainAddressKey(request.ChainName, request.Target)
case request.ChainName != "":
ownersPrefix = types.ChainLinkChainKey(request.ChainName)
}

var owners []types.QueryChainLinkOwnersResponse_ChainLinkOwnerDetails
ownersStore := prefix.NewStore(store, ownersPrefix)
pageRes, err := query.Paginate(ownersStore, request.Pagination, func(key []byte, value []byte) error {
keyWithPrefix := append(ownersPrefix, key...)
cleanedKey := bytes.TrimSuffix(bytes.TrimPrefix(keyWithPrefix, types.ChainLinkChainPrefix), value)
values := bytes.Split(cleanedKey, types.Separator)
chainName, target := values[0], values[1]
Copy link
Contributor

@dadamu dadamu Mar 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it can be all implemented by bytes.Split instead of bytes.TrimSuffix, say:

values := bytes.Split(cleanedKey, types.Separator)
chainName, target, value := values[0], values[1], values[2]

Then, the value of the key can be simply 0x01 in order to reduce the storage on chain.

Copy link
Contributor Author

@RiccardoM RiccardoM Mar 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now, links owners are stored using the following keys:

ChainLinkChainPrefix + Chain name + Separator + External Address + User address -> User address

So, if we use the bytes.Split it would return the following values:

1. Chain name
2. External Address + User address

We would have to remove the User address suffix anyway to get the external address.

Also, the key for chain link owners right now are:

ChainLinkChainPrefix     = []byte{0x15}
ApplicationLinkAppPrefix = []byte{0x16}

Which already are 1 byte each. We already use 0x01 for the IBCPortKey key. We can't re-use that. Also, 0x01 and 0x16 are both 1 byte each so that wouldn't impact the chain storage space

Copy link
Contributor

@dadamu dadamu Mar 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RiccardoM Sorry for that the explanation was not clear, I mean that we can store the key like:

ChainLinkChainPrefix + Chain name + Separator + External Address + Spearator + User address -> 0x01

Then the bytes.Split would return the values:

1. Chain name
2. External address
3. User address

As a result, the storage doesn't need to store user address as value again.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dadamu You are completely right! Thanks for pointing that out, this way we can save up a lot of disk space. I've just implemented it this way (and also added some missing tests)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering this answer, should we look after other places where we use bytes.TrimSuffix and bytes.TrimPrefix and see if we can improve them as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bragaz All other keys instances do not trim prefixes unless necessary (eg. inside user group members). All the keys have already been reviewed inside their own PRs before being merged, and I don't think it would be appropriate to do it here anyway. I've checked though and didn't find any use of bytes.TrimPrefix aside from this case and user groups members anyway


owners = append(owners, types.QueryChainLinkOwnersResponse_ChainLinkOwnerDetails{
User: string(value),
ChainName: string(chainName),
Target: string(target),
})

return nil
})

if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

return &types.QueryChainLinkOwnersResponse{Owners: owners, Pagination: pageRes}, nil
}

// ApplicationLinks implements the Query/ApplicationLinks gRPC method
func (k Keeper) ApplicationLinks(ctx context.Context, request *types.QueryApplicationLinksRequest) (*types.QueryApplicationLinksResponse, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
Expand Down Expand Up @@ -174,6 +212,43 @@ func (k Keeper) ApplicationLinkByClientID(ctx context.Context, request *types.Qu
return &types.QueryApplicationLinkByClientIDResponse{Link: link}, nil
}

// ApplicationLinkOwners implements the Query/ApplicationLinkOwners gRPC method
func (k Keeper) ApplicationLinkOwners(ctx context.Context, request *types.QueryApplicationLinkOwnersRequest) (*types.QueryApplicationLinkOwnersResponse, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
store := sdkCtx.KVStore(k.storeKey)

ownersPrefix := types.ApplicationLinkAppPrefix
switch {
case request.Application != "" && request.Username != "":
ownersPrefix = types.ApplicationLinkAppUsernameKey(request.Application, request.Username)
case request.Application != "":
ownersPrefix = types.ApplicationLinkAppKey(request.Application)
}

var owners []types.QueryApplicationLinkOwnersResponse_ApplicationLinkOwnerDetails
ownersStore := prefix.NewStore(store, ownersPrefix)
pageRes, err := query.Paginate(ownersStore, request.Pagination, func(key []byte, value []byte) error {
keyWithPrefix := append(ownersPrefix, key...)
cleanedKey := bytes.TrimSuffix(bytes.TrimPrefix(keyWithPrefix, types.ApplicationLinkAppPrefix), value)
values := bytes.Split(cleanedKey, types.Separator)
application, username := values[0], values[1]

owners = append(owners, types.QueryApplicationLinkOwnersResponse_ApplicationLinkOwnerDetails{
User: string(value),
Application: string(application),
Username: string(username),
})

return nil
})

if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

return &types.QueryApplicationLinkOwnersResponse{Owners: owners, Pagination: pageRes}, nil
}

// Params implements the Query/Params gRPC method
func (k Keeper) Params(ctx context.Context, _ *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
Expand Down
Loading