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: add pagination to sensitive endpoints #1601

Merged
merged 2 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
402 changes: 402 additions & 0 deletions internal/collcompat/collections_pagination.go

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions proto/dymensionxyz/dymension/eibc/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ message QueryDemandOrdersByStatusRequest {
string denom = 7;
// optional recipient address
string recipient = 8;
cosmos.base.query.v1beta1.PageRequest pagination = 9;
}

enum FulfillmentState {
Expand All @@ -78,4 +79,5 @@ message QueryGetDemandOrderResponse {
message QueryDemandOrdersByStatusResponse {
// A list of demand orders with the given status
repeated DemandOrder demand_orders = 1;
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
3 changes: 3 additions & 0 deletions proto/dymensionxyz/dymension/iro/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "cosmos_proto/cosmos.proto";
import "google/api/annotations.proto";
import "dymensionxyz/dymension/iro/iro.proto";
import "cosmos/base/v1beta1/coin.proto";
import "cosmos/base/query/v1beta1/pagination.proto";

option go_package = "github.com/dymensionxyz/dymension/v3/x/iro/types";

Expand Down Expand Up @@ -70,11 +71,13 @@ message QueryParamsResponse {
message QueryPlansRequest {
// tradable_only is an optional flag to filter out plans that have not started yet, and that are not settled.
bool tradable_only = 1;
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}

// QueryPlanResponse is the response type for the Query/QueryPlan RPC method.
message QueryPlansResponse {
repeated Plan plans = 1 [ (gogoproto.nullable) = false ];
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

// QueryPlanRequest is the request type for the Query/QueryPlan RPC method.
Expand Down
2 changes: 2 additions & 0 deletions proto/dymensionxyz/dymension/rollapp/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,12 @@ message QueryGetStateInfoResponse {

message QueryRegisteredDenomsRequest {
string rollappId = 1;
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}

message QueryRegisteredDenomsResponse {
repeated string denoms = 1;
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

message QueryObsoleteDRSVersionsRequest {}
Expand Down
8 changes: 7 additions & 1 deletion proto/dymensionxyz/dymension/sequencer/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -88,19 +88,25 @@ message QuerySequencersResponse {
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

message QueryGetSequencersByRollappRequest { string rollappId = 1; }
message QueryGetSequencersByRollappRequest {
string rollappId = 1;
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}

message QueryGetSequencersByRollappResponse {
repeated Sequencer sequencers = 1 [ (gogoproto.nullable) = false ];
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

message QueryGetSequencersByRollappByStatusRequest {
string rollappId = 1;
OperatingStatus status = 2;
cosmos.base.query.v1beta1.PageRequest pagination = 3;
}

message QueryGetSequencersByRollappByStatusResponse {
repeated Sequencer sequencers = 1 [ (gogoproto.nullable) = false ];
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

// Request type for the GetProposerByRollapp RPC method.
Expand Down
4 changes: 2 additions & 2 deletions x/delayedack/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,13 @@ func (q Querier) GetPendingPacketsByAddress(goCtx context.Context, req *types.Qu
ctx := sdk.UnwrapSDKContext(goCtx)

// Get all pending rollapp packets until the latest finalized height
p, err := q.Keeper.GetPendingPacketsByAddress(ctx, req.Address)
p, pageResp, err := q.Keeper.GetPendingPacketsByAddressPaginated(ctx, req.Address, req.Pagination)
if err != nil {
return nil, fmt.Errorf("get pending packets by receiver %s: %w", req.Address, err)
}

return &types.QueryPendingPacketByAddressListResponse{
RollappPackets: p,
Pagination: nil, // TODO: handle pagination
Pagination: pageResp,
}, nil
}
21 changes: 21 additions & 0 deletions x/delayedack/keeper/rollapp_packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import (

"cosmossdk.io/collections"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"

"github.com/dymensionxyz/gerr-cosmos/gerrc"

"github.com/dymensionxyz/dymension/v3/internal/collcompat"
commontypes "github.com/dymensionxyz/dymension/v3/x/common/types"
"github.com/dymensionxyz/dymension/v3/x/delayedack/types"
)
Expand Down Expand Up @@ -77,6 +79,25 @@ func (k Keeper) GetPendingPacketsByAddress(ctx sdk.Context, receiver string) ([]
return packets, nil
}

// GetPendingPacketsByAddressPaginated retrieves rollapp packets from the KVStore by their receiver with pagination.
func (k Keeper) GetPendingPacketsByAddressPaginated(ctx sdk.Context, receiver string, pageReq *query.PageRequest) (packets []commontypes.RollappPacket, pageResp *query.PageResponse, err error) {
_, pageResp, err = collcompat.CollectionPaginate(ctx, k.pendingPacketsByAddress, pageReq,
func(key collections.Pair[string, []byte], _ collections.NoValue) (bool, error) {
packet, err := k.GetRollappPacket(ctx, string(key.K2()))
if err != nil {
return true, err
}
packets = append(packets, *packet)
return false, nil
}, collcompat.WithCollectionPaginationPairPrefix[string, []byte](receiver),
)
if err != nil {
return nil, nil, err
}

return packets, pageResp, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

this could be simpler. you can directly return commontypes.RollappPacket in transformFunc while paginating.

Suggested change
// GetPendingPacketsByAddressPaginated retrieves rollapp packets from the KVStore by their receiver with pagination.
func (k Keeper) GetPendingPacketsByAddressPaginated(ctx sdk.Context, receiver string, pageReq *query.PageRequest) (packets []commontypes.RollappPacket, pageResp *query.PageResponse, err error) {
_, pageResp, err = collcompat.CollectionPaginate(ctx, k.pendingPacketsByAddress, pageReq,
func(key collections.Pair[string, []byte], _ collections.NoValue) (bool, error) {
packet, err := k.GetRollappPacket(ctx, string(key.K2()))
if err != nil {
return true, err
}
packets = append(packets, *packet)
return false, nil
}, collcompat.WithCollectionPaginationPairPrefix[string, []byte](receiver),
)
if err != nil {
return nil, nil, err
}
return packets, pageResp, nil
}
// GetPendingPacketsByAddressPaginated retrieves rollapp packets from the KVStore by their receiver with pagination.
func (k Keeper) GetPendingPacketsByAddressPaginated(ctx sdk.Context, receiver string, pageReq *query.PageRequest) ([]commontypes.RollappPacket, *query.PageResponse, error) {
return collcompat.CollectionPaginate(ctx, k.pendingPacketsByAddress, pageReq,
func(key collections.Pair[string, []byte], _ collections.NoValue) (commontypes.RollappPacket, error) {
packet, err := k.GetRollappPacket(ctx, string(key.K2()))
return *packet, err
}, collcompat.WithCollectionPaginationPairPrefix[string, []byte](receiver),
)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

packet, err := k.GetRollappPacket(ctx, string(key.K2()))
return *packet, err

packet could be nil though


// GetRollappPacket retrieves a rollapp packet from the KVStore.
func (k Keeper) GetRollappPacket(ctx sdk.Context, rollappPacketKey string) (*commontypes.RollappPacket, error) {
store := ctx.KVStore(k.storeKey)
Expand Down
12 changes: 9 additions & 3 deletions x/eibc/client/cli/query_command_orders.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,18 @@ func CmdListDemandOrdersByStatus() *cobra.Command {
if !ok {
return fmt.Errorf("invalid status: %s", args[0])
}

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

request := &types.QueryDemandOrdersByStatusRequest{
Status: commontypes.Status(status),
Type: commontypes.RollappPacket_UNDEFINED, // default to undefined, as '0' is a valid type
Status: commontypes.Status(status),
Type: commontypes.RollappPacket_UNDEFINED, // default to undefined, as '0' is a valid type
Pagination: pageReq,
}

var err error
request.RollappId, err = cmd.Flags().GetString("rollapp")
if err != nil {
return err
Expand Down
7 changes: 5 additions & 2 deletions x/eibc/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,15 @@ func (q Querier) DemandOrdersByStatus(goCtx context.Context, req *types.QueryDem
return nil, status.Error(codes.InvalidArgument, "empty request")
}
// Get the demand orders by status, with optional filters
demandOrders, err := q.ListDemandOrdersByStatus(sdk.UnwrapSDKContext(goCtx), req.Status, int(req.Limit), filterOpts(req)...)
demandOrders, pageResp, err := q.ListDemandOrdersByStatusPaginated(sdk.UnwrapSDKContext(goCtx), req.Status, req.Pagination, filterOpts(req)...)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
// Construct the response
return &types.QueryDemandOrdersByStatusResponse{DemandOrders: demandOrders}, nil
return &types.QueryDemandOrdersByStatusResponse{
DemandOrders: demandOrders,
Pagination: pageResp,
}, nil
}

func filterOpts(req *types.QueryDemandOrdersByStatusRequest) []filterOption {
Expand Down
44 changes: 44 additions & 0 deletions x/eibc/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import (

"github.com/cometbft/cometbft/libs/log"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
"github.com/dymensionxyz/sdk-utils/utils/uevent"

Expand Down Expand Up @@ -198,6 +200,48 @@ outer:
return list, nil
}

func (k Keeper) ListDemandOrdersByStatusPaginated(
ctx sdk.Context,
status commontypes.Status,
pageReq *query.PageRequest,
opts ...filterOption,
) (list []*types.DemandOrder, pageResp *query.PageResponse, err error) {
store := ctx.KVStore(k.storeKey)

var statusPrefix []byte
switch status {
case commontypes.Status_PENDING:
statusPrefix = types.PendingDemandOrderKeyPrefix
case commontypes.Status_FINALIZED:
statusPrefix = types.FinalizedDemandOrderKeyPrefix
default:
err = fmt.Errorf("invalid demand order status: %s", status)
return
}

prefixStore := prefix.NewStore(store, statusPrefix)

if pageReq == nil {
pageReq = &query.PageRequest{}
}

pageResp, err = query.Paginate(prefixStore, pageReq, func(key []byte, value []byte) error {
var val types.DemandOrder
if err := k.cdc.Unmarshal(value, &val); err != nil {
return err
}
for _, opt := range opts {
if !opt(val) {
return nil
}
}
list = append(list, &val)
return nil
})

return
}

/* -------------------------------------------------------------------------- */
/* Hooks handling */
/* -------------------------------------------------------------------------- */
Expand Down
Loading
Loading