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: mapped account query #443

Merged
merged 11 commits into from
Jun 2, 2023
130 changes: 130 additions & 0 deletions docs/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2379,6 +2379,136 @@ paths:
type: string
tags:
- Msg
/quicksilver/interchainstaking/v1/mapped_addresses/{address}:
get:
summary: >-
MappedAccounts provides data on the mapped accounts for a given user
over different host chains.
operationId: MappedAccounts
responses:
'200':
description: A successful response.
schema:
type: object
properties:
RemoteAddressMap:
type: object
additionalProperties:
type: string
format: byte
pagination:
type: object
properties:
next_key:
type: string
format: byte
description: |-
next_key is the key to be passed to PageRequest.key to
query the next page most efficiently. It will be empty if
there are no more results.
total:
type: string
format: uint64
title: >-
total is total number of results available if
PageRequest.count_total

was set, its value is undefined otherwise
description: >-
PageResponse is to be embedded in gRPC response messages where
the

corresponding request message has used PageRequest.

message SomeResponse {
repeated Bar results = 1;
PageResponse page = 2;
}
default:
description: An unexpected error response.
schema:
type: object
properties:
error:
type: string
code:
type: integer
format: int32
message:
type: string
details:
type: array
items:
type: object
properties:
type_url:
type: string
value:
type: string
format: byte
parameters:
- name: address
in: path
required: true
type: string
- name: pagination.key
description: |-
key is a value returned in PageResponse.next_key to begin
querying the next page most efficiently. Only one of offset or key
should be set.
in: query
required: false
type: string
format: byte
- name: pagination.offset
description: >-
offset is a numeric offset that can be used when key is unavailable.

It is less efficient than using key. Only one of offset or key
should

be set.
in: query
required: false
type: string
format: uint64
- name: pagination.limit
description: >-
limit is the total number of results to be returned in the result
page.

If left empty it will default to a value to be set by each app.
in: query
required: false
type: string
format: uint64
- name: pagination.count_total
description: >-
count_total is set to true to indicate that the result set should
include

a count of the total number of items available for pagination in
UIs.

count_total is only respected when offset is used. It is ignored
when key

is set.
in: query
required: false
type: boolean
- name: pagination.reverse
description: >-
reverse is set to true if results are to be returned in the
descending order.


Since: cosmos-sdk 0.43
in: query
required: false
type: boolean
tags:
- QueryInterchainStaking
/quicksilver/interchainstaking/v1/zone/{chain_id}:
get:
summary: Zone provides meta data on a specific zone.
Expand Down
15 changes: 15 additions & 0 deletions proto/quicksilver/interchainstaking/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ service Query {
"/quicksilver/interchainstaking/v1/zones/"
"{chain_id}/redelegation_records";
}

// MappedAccounts provides data on the mapped accounts for a given user over different host chains.
rpc MappedAccounts(QueryMappedAccountsRequest) returns (QueryMappedAccountsResponse) {
option (google.api.http).get = "/quicksilver/interchainstaking/v1/mapped_addresses/{address}";
}
}

message Statistics {
Expand Down Expand Up @@ -184,3 +189,13 @@ message QueryRedelegationRecordsResponse {
repeated RedelegationRecord redelegations = 1 [(gogoproto.nullable) = false];
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

message QueryMappedAccountsRequest {
string address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}

message QueryMappedAccountsResponse {
map<string, bytes> RemoteAddressMap= 1 [(gogoproto.nullable) = false];
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
35 changes: 35 additions & 0 deletions x/interchainstaking/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func GetQueryCmd() *cobra.Command {
GetCmdZones(),
GetDelegatorIntentCmd(),
GetDepositAccountCmd(),
GetMappedAccountsCmd(),
)

return cmd
Expand Down Expand Up @@ -144,3 +145,37 @@ func GetDepositAccountCmd() *cobra.Command {

return cmd
}

// GetMappedAccountsCmd returns the mapped account for the given address.
func GetMappedAccountsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "mapped-accounts [address]",
Short: "Query mapped accounts for a given address.",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

// args
address := args[0]

queryClient := types.NewQueryClient(clientCtx)
req := &types.QueryMappedAccountsRequest{
Address: address,
}

res, err := queryClient.MappedAccounts(cmd.Context(), req)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}
24 changes: 24 additions & 0 deletions x/interchainstaking/keeper/address_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,30 @@ func (k *Keeper) GetRemoteAddressMap(ctx sdk.Context, localAddress []byte, chain
return value, value != nil
}

// IterateUserMappedAccounts iterates over all the user mapped accounts.
func (k Keeper) IterateUserMappedAccounts(ctx sdk.Context, localAddress []byte, fn func(index int64, chainID string, remoteAddressBytes []byte) (stop bool)) {
// noop
if fn == nil {
return
}

store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.GetRemoteAddressPrefix(localAddress))
defer iterator.Close()

i := int64(0)
for ; iterator.Valid(); iterator.Next() {
value := iterator.Value()
key := iterator.Key()
chainIDBytes := key[len(types.GetRemoteAddressPrefix(localAddress)):]
ajansari95 marked this conversation as resolved.
Show resolved Hide resolved
stop := fn(i, string(chainIDBytes), value)
if stop {
break
}
i++
}
}

// SetRemoteAddressMap sets a remote address using a local address as a map.
func (k *Keeper) SetRemoteAddressMap(ctx sdk.Context, localAddress, remoteAddress []byte, chainID string) {
store := ctx.KVStore(k.storeKey)
Expand Down
22 changes: 22 additions & 0 deletions x/interchainstaking/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/ingenuity-build/quicksilver/utils"
"github.com/ingenuity-build/quicksilver/x/interchainstaking/types"
)

Expand Down Expand Up @@ -254,3 +255,24 @@ func (k *Keeper) RedelegationRecords(c context.Context, req *types.QueryRedelega

return &types.QueryRedelegationRecordsResponse{Redelegations: redelegations}, nil
}

func (k *Keeper) MappedAccounts(c context.Context, req *types.QueryMappedAccountsRequest) (*types.QueryMappedAccountsResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

ctx := sdk.UnwrapSDKContext(c)

remoteAddressMap := make(map[string][]byte)
addrBytes, err := utils.AccAddressFromBech32(req.Address, sdk.GetConfig().GetBech32AccountAddrPrefix())
if err != nil {
return nil, status.Error(codes.InvalidArgument, "Invalid Address")
}

k.IterateUserMappedAccounts(ctx, addrBytes, func(index int64, chainID string, remoteAddressBytes []byte) (stop bool) {
remoteAddressMap[chainID] = remoteAddressBytes
return false
})

return &types.QueryMappedAccountsResponse{RemoteAddressMap: remoteAddressMap}, nil
}
Loading