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: Query host chain user unbondings #721

Merged
merged 6 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ Ref: https://keepachangelog.com/en/1.0.0/

## Unreleased

### Features

- [721](https://github.com/persistenceOne/pstake-native/pull/721) Add Query host chain user unbondings.

### Bug Fixes

- [720](https://github.com/persistenceOne/pstake-native/pull/720) Fix unbondings loop.
Expand Down
17 changes: 17 additions & 0 deletions proto/pstake/liquidstakeibc/v1beta1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ service Query {
"/pstake/liquidstakeibc/v1beta1/user_unbondings/{address}";
}

// Queries all unbondings for a host chain.
rpc HostChainUserUnbondings(QueryHostChainUserUnbondingsRequest)
returns (QueryHostChainUserUnbondingsResponse) {
option (google.api.http).get =
"/pstake/liquidstakeibc/v1beta1/user_unbondings/{chain_id}";
}

// Queries all validator unbondings for a host chain.
rpc ValidatorUnbondings(QueryValidatorUnbondingRequest)
returns (QueryValidatorUnbondingResponse) {
Expand Down Expand Up @@ -142,6 +149,16 @@ message QueryUserUnbondingsResponse {
repeated UserUnbonding user_unbondings = 1;
}

message QueryHostChainUserUnbondingsRequest {
string chain_id = 1;
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}

message QueryHostChainUserUnbondingsResponse {
repeated UserUnbonding user_unbondings = 1;
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

message QueryValidatorUnbondingRequest { string chain_id = 1; }

message QueryValidatorUnbondingResponse {
Expand Down
47 changes: 47 additions & 0 deletions x/liquidstakeibc/client/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func NewQueryCmd() *cobra.Command {
QueryLSMDepositsCmd(),
QueryUnbondingsCmd(),
QueryUserUnbondingsCmd(),
QueryHostChainUserUnbondingsCmd(),
QueryValidatorUnbondingsCmd(),
QueryDepositAccountBalanceCmd(),
QueryExchangeRateCmd(),
Expand Down Expand Up @@ -298,6 +299,52 @@ func QueryUserUnbondingsCmd() *cobra.Command {
return cmd
}

// QueryHostChainUserUnbondingsCmd returns all user unbondings for a host chain.
func QueryHostChainUserUnbondingsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "host-chain-user-unbondings [chain-id]",
Short: "Query all user unbonding records for a host chain",
Args: cobra.ExactArgs(1),
Long: strings.TrimSpace(
fmt.Sprintf(
`Query all user unbonding records for a host chain: $ %s query liquidstakeibc host-chain-user-unbondings [chain-id]`,
version.AppName,
),
),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

pageReq, err := client.ReadPageRequest(cmd.Flags())
if err != nil {
return err
}

queryClient := types.NewQueryClient(clientCtx)

res, err := queryClient.HostChainUserUnbondings(
context.Background(),
&types.QueryHostChainUserUnbondingsRequest{
ChainId: args[0],
Pagination: pageReq,
},
)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddPaginationFlagsToCmd(cmd, cmd.Use)
flags.AddQueryFlagsToCmd(cmd)

return cmd
}

// QueryValidatorUnbondingsCmd returns all validator unbondings for a host chain.
func QueryValidatorUnbondingsCmd() *cobra.Command {
cmd := &cobra.Command{
Expand Down
47 changes: 47 additions & 0 deletions x/liquidstakeibc/keeper/grpc_querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import (
"context"

"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/query"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -177,6 +179,51 @@
return &types.QueryUserUnbondingsResponse{UserUnbondings: userUnbondings}, nil
}

func (k *Keeper) HostChainUserUnbondings(
goCtx context.Context,
request *types.QueryHostChainUserUnbondingsRequest,
) (*types.QueryHostChainUserUnbondingsResponse, error) {
if request == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
if request.ChainId == "" {
return nil, status.Error(codes.InvalidArgument, "chain id cannot be empty")
}

ctx := sdk.UnwrapSDKContext(goCtx)

store := ctx.KVStore(k.storeKey)
userUnbondingStore := prefix.NewStore(store, types.UserUnbondingKey)

var userUnbondings []*types.UserUnbonding
pageRes, err := query.FilteredPaginate(
userUnbondingStore,
request.Pagination,
func(key []byte, value []byte, accumulate bool) (bool, error) {

Check failure on line 202 in x/liquidstakeibc/keeper/grpc_querier.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofumpt`-ed with `-extra` (gofumpt)
if accumulate {
var uu types.UserUnbonding
if err := k.cdc.Unmarshal(value, &uu); err != nil {
return false, err
}

if uu.ChainId == request.ChainId {
userUnbondings = append(userUnbondings, &uu)
return true, nil
}

return false, nil
}

return true, nil
})

Check failure on line 219 in x/liquidstakeibc/keeper/grpc_querier.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofumpt`-ed with `-extra` (gofumpt)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

return &types.QueryHostChainUserUnbondingsResponse{UserUnbondings: userUnbondings, Pagination: pageRes}, nil
}

func (k *Keeper) ValidatorUnbondings(
goCtx context.Context,
request *types.QueryValidatorUnbondingRequest,
Expand Down
100 changes: 99 additions & 1 deletion x/liquidstakeibc/keeper/grpc_querier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package keeper_test

import (
"strconv"
"testing"

sdktypes "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/query"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/bank/testutil"
"google.golang.org/grpc/codes"
Expand All @@ -14,7 +16,9 @@ import (
)

const (
MultipleTestSize int = 10
MultipleTestSize int = 10
PaginatedTestSize int = 98
PaginatedStep int = 5

TestAddress string = "persistence1xruvjju28j0a5ud5325rfdak8f5a04h0s30mld"
)
Expand Down Expand Up @@ -315,6 +319,100 @@ func (suite *IntegrationTestSuite) TestQueryUserUnbondings() {
}
}

func (suite *IntegrationTestSuite) TestQueryHostChainUserUnbondings() {
chainAUserUnbondings := make([]*types.UserUnbonding, 0)
for i := 0; i < PaginatedTestSize; i += 1 {
userUnbonding := &types.UserUnbonding{
ChainId: suite.chainA.ChainID,
Address: TestAddress,
EpochNumber: int64(i),
}
suite.app.LiquidStakeIBCKeeper.SetUserUnbonding(suite.ctx, userUnbonding)
chainAUserUnbondings = append(chainAUserUnbondings, userUnbonding)
}

chainBUserUnbondings := make([]*types.UserUnbonding, 0)
for i := 0; i < PaginatedTestSize; i += 1 {
userUnbonding := &types.UserUnbonding{
ChainId: suite.chainB.ChainID,
Address: TestAddress,
EpochNumber: int64(i),
}
suite.app.LiquidStakeIBCKeeper.SetUserUnbonding(suite.ctx, userUnbonding)
chainBUserUnbondings = append(chainBUserUnbondings, userUnbonding)
}

request := func(
chainID string,
next []byte,
offset, limit uint64,
total bool,
) *types.QueryHostChainUserUnbondingsRequest {
return &types.QueryHostChainUserUnbondingsRequest{
ChainId: chainID,
Pagination: &query.PageRequest{Key: next, Offset: offset, Limit: limit, CountTotal: total},
}
}

suite.T().Run("ByOffset", func(t *testing.T) {
for i := 0; i < PaginatedTestSize; i += PaginatedStep {
resp, err := suite.app.LiquidStakeIBCKeeper.HostChainUserUnbondings(
suite.ctx,
request(suite.chainB.ChainID, nil, uint64(i), uint64(PaginatedStep), false),
)
suite.Require().NoError(err)
suite.Require().LessOrEqual(len(resp.UserUnbondings), PaginatedStep)
suite.Require().Subset(chainBUserUnbondings, resp.UserUnbondings)
}
})

suite.T().Run("ByKey", func(t *testing.T) {
var next []byte
for i := 0; i < PaginatedTestSize; i += PaginatedStep {
resp, err := suite.app.LiquidStakeIBCKeeper.HostChainUserUnbondings(
suite.ctx,
request(suite.chainA.ChainID, next, 0, uint64(PaginatedStep), false),
)
suite.Require().NoError(err)
suite.Require().LessOrEqual(len(resp.UserUnbondings), PaginatedStep)
suite.Require().Subset(chainAUserUnbondings, resp.UserUnbondings)
next = resp.Pagination.NextKey
}
})

suite.T().Run("Total", func(t *testing.T) {
resp, err := suite.app.LiquidStakeIBCKeeper.HostChainUserUnbondings(
suite.ctx,
request(suite.chainB.ChainID, nil, 0, 0, true),
)
suite.Require().NoError(err)
suite.Require().Equal(len(chainBUserUnbondings), int(resp.Pagination.Total))
suite.Require().ElementsMatch(chainBUserUnbondings, resp.UserUnbondings)
})

suite.T().Run("Total Empty", func(t *testing.T) {
resp, err := suite.app.LiquidStakeIBCKeeper.HostChainUserUnbondings(
suite.ctx,
request("non-existing-chain", nil, 0, 0, true),
)
suite.Require().NoError(err)
suite.Require().Equal(0, int(resp.Pagination.Total))
})

suite.T().Run("Invalid Request", func(t *testing.T) {
_, err := suite.app.LiquidStakeIBCKeeper.HostChainUserUnbondings(
suite.ctx,
request("", nil, 0, 0, true),
)
suite.Require().ErrorIs(err, status.Error(codes.InvalidArgument, "chain id cannot be empty"))
})

suite.T().Run("Invalid Request", func(t *testing.T) {
_, err := suite.app.LiquidStakeIBCKeeper.HostChainUserUnbondings(suite.ctx, nil)
suite.Require().ErrorIs(err, status.Error(codes.InvalidArgument, "empty request"))
})
}

func (suite *IntegrationTestSuite) TestQueryValidatorUnbondings() {
validatorUnbondings := make([]*types.ValidatorUnbonding, 0)
for i := 0; i < MultipleTestSize; i += 1 {
Expand Down
Loading
Loading