Skip to content

Commit

Permalink
feat: add paginated packet commitments query to channel/v2 (#7533)
Browse files Browse the repository at this point in the history
* feat: add paginated packet commitments query to channel/v2

* chore: add cli handler

* chore: rm commented code

* chore: make lint-fix

---------

Co-authored-by: DimitrisJim <[email protected]>
  • Loading branch information
damiannolan and DimitrisJim authored Nov 11, 2024
1 parent 9239d5d commit cae86e6
Show file tree
Hide file tree
Showing 9 changed files with 987 additions and 46 deletions.
1 change: 1 addition & 0 deletions modules/core/04-channel/v2/client/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func GetQueryCmd() *cobra.Command {
queryCmd.AddCommand(
getCmdQueryChannel(),
getCmdQueryPacketCommitment(),
getCmdQueryPacketCommitments(),
getCmdQueryPacketAcknowledgement(),
getCmdQueryPacketReceipt(),
)
Expand Down
39 changes: 39 additions & 0 deletions modules/core/04-channel/v2/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,45 @@ func getCmdQueryPacketCommitment() *cobra.Command {
return cmd
}

func getCmdQueryPacketCommitments() *cobra.Command {
cmd := &cobra.Command{
Use: "packet-commitments [channel-id]",
Short: "Query all packet commitments associated with a channel",
Long: "Query all packet commitments associated with a channel",
Example: fmt.Sprintf("%s query %s %s packet-commitments [channel-id]", version.AppName, exported.ModuleName, types.SubModuleName),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

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

req := &types.QueryPacketCommitmentsRequest{
ChannelId: args[0],
Pagination: pageReq,
}

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

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)
flags.AddPaginationFlagsToCmd(cmd, "packet commitments associated with a channel")

return cmd
}

func getCmdQueryPacketAcknowledgement() *cobra.Command {
cmd := &cobra.Command{
Use: "packet-acknowledgement [channel-id] [sequence]",
Expand Down
48 changes: 48 additions & 0 deletions modules/core/04-channel/v2/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@ package keeper

import (
"context"
"strings"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

errorsmod "cosmossdk.io/errors"
"cosmossdk.io/store/prefix"

"github.com/cosmos/cosmos-sdk/runtime"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"

clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types"
"github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types"
host "github.com/cosmos/ibc-go/v9/modules/core/24-host"
hostv2 "github.com/cosmos/ibc-go/v9/modules/core/24-host/v2"
)

var _ types.QueryServer = (*queryServer)(nil)
Expand Down Expand Up @@ -71,6 +78,47 @@ func (q *queryServer) PacketCommitment(ctx context.Context, req *types.QueryPack
return types.NewQueryPacketCommitmentResponse(commitment, nil, clienttypes.GetSelfHeight(ctx)), nil
}

// PacketCommitments implements the Query/PacketCommitments gRPC method
func (q *queryServer) PacketCommitments(ctx context.Context, req *types.QueryPacketCommitmentsRequest) (*types.QueryPacketCommitmentsResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

if err := host.ChannelIdentifierValidator(req.ChannelId); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}

if !q.HasChannel(ctx, req.ChannelId) {
return nil, status.Error(codes.NotFound, errorsmod.Wrap(types.ErrChannelNotFound, req.ChannelId).Error())
}

var commitments []*types.PacketState
store := prefix.NewStore(runtime.KVStoreAdapter(q.storeService.OpenKVStore(ctx)), hostv2.PacketCommitmentPrefixKey(req.ChannelId))

pageRes, err := query.Paginate(store, req.Pagination, func(key, value []byte) error {
keySplit := strings.Split(string(key), "/")

sequence := sdk.BigEndianToUint64([]byte(keySplit[len(keySplit)-1]))
if sequence == 0 {
return types.ErrInvalidPacket
}

commitment := types.NewPacketState(req.ChannelId, sequence, value)
commitments = append(commitments, &commitment)
return nil
})
if err != nil {
return nil, err
}

selfHeight := clienttypes.GetSelfHeight(ctx)
return &types.QueryPacketCommitmentsResponse{
Commitments: commitments,
Pagination: pageRes,
Height: selfHeight,
}, nil
}

// PacketAcknowledgement implements the Query/PacketAcknowledgement gRPC method.
func (q *queryServer) PacketAcknowledgement(ctx context.Context, req *types.QueryPacketAcknowledgementRequest) (*types.QueryPacketAcknowledgementResponse, error) {
if req == nil {
Expand Down
115 changes: 115 additions & 0 deletions modules/core/04-channel/v2/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/cosmos/cosmos-sdk/types/query"

"github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/keeper"
"github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types"
commitmenttypes "github.com/cosmos/ibc-go/v9/modules/core/23-commitment/types"
Expand Down Expand Up @@ -191,6 +193,119 @@ func (suite *KeeperTestSuite) TestQueryPacketCommitment() {
}
}

func (suite *KeeperTestSuite) TestQueryPacketCommitments() {
var (
req *types.QueryPacketCommitmentsRequest
expCommitments = []*types.PacketState{}
)

testCases := []struct {
msg string
malleate func()
expError error
}{
{
"success",
func() {
path := ibctesting.NewPath(suite.chainA, suite.chainB)
path.SetupV2()

expCommitments = make([]*types.PacketState, 0, 10) // reset expected commitments
for i := uint64(1); i <= 10; i++ {
pktStateCommitment := types.NewPacketState(path.EndpointA.ChannelID, i, []byte(fmt.Sprintf("hash_%d", i)))
suite.chainA.App.GetIBCKeeper().ChannelKeeperV2.SetPacketCommitment(suite.chainA.GetContext(), pktStateCommitment.ChannelId, pktStateCommitment.Sequence, pktStateCommitment.Data)
expCommitments = append(expCommitments, &pktStateCommitment)
}

req = &types.QueryPacketCommitmentsRequest{
ChannelId: path.EndpointA.ChannelID,
Pagination: &query.PageRequest{
Key: nil,
Limit: 11,
CountTotal: true,
},
}
},
nil,
},
{
"success: with pagination",
func() {
path := ibctesting.NewPath(suite.chainA, suite.chainB)
path.SetupV2()

expCommitments = make([]*types.PacketState, 0, 10) // reset expected commitments
for i := uint64(1); i <= 10; i++ {
pktStateCommitment := types.NewPacketState(path.EndpointA.ChannelID, i, []byte(fmt.Sprintf("hash_%d", i)))
suite.chainA.App.GetIBCKeeper().ChannelKeeperV2.SetPacketCommitment(suite.chainA.GetContext(), pktStateCommitment.ChannelId, pktStateCommitment.Sequence, pktStateCommitment.Data)
expCommitments = append(expCommitments, &pktStateCommitment)
}

limit := uint64(5)
expCommitments = expCommitments[:limit]

req = &types.QueryPacketCommitmentsRequest{
ChannelId: path.EndpointA.ChannelID,
Pagination: &query.PageRequest{
Key: nil,
Limit: limit,
CountTotal: true,
},
}
},
nil,
},
{
"empty request",
func() {
req = nil
},
status.Error(codes.InvalidArgument, "empty request"),
},
{
"invalid channel ID",
func() {
req = &types.QueryPacketCommitmentsRequest{
ChannelId: "",
}
},
status.Error(codes.InvalidArgument, "identifier cannot be blank: invalid identifier"),
},
{
"channel not found",
func() {
req = &types.QueryPacketCommitmentsRequest{
ChannelId: "channel-141",
}
},
status.Error(codes.NotFound, fmt.Sprintf("%s: channel not found", "channel-141")),
},
}

for _, tc := range testCases {
tc := tc

suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
suite.SetupTest() // reset

tc.malleate()
ctx := suite.chainA.GetContext()

queryServer := keeper.NewQueryServer(suite.chainA.GetSimApp().IBCKeeper.ChannelKeeperV2)
res, err := queryServer.PacketCommitments(ctx, req)

expPass := tc.expError == nil
if expPass {
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(expCommitments, res.Commitments)
} else {
suite.Require().Error(err)
}
})
}
}

func (suite *KeeperTestSuite) TestQueryPacketAcknowledgement() {
var (
expAcknowledgement []byte
Expand Down
9 changes: 9 additions & 0 deletions modules/core/04-channel/v2/types/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,12 @@ func (p Payload) ValidateBasic() error {
func TimeoutTimestampToNanos(seconds uint64) uint64 {
return uint64(time.Unix(int64(seconds), 0).UnixNano())
}

// NewPacketState creates and returns a new PacketState envelope type to encapsulate packet commitments, acks or receipts.
func NewPacketState(channelID string, sequence uint64, data []byte) PacketState {
return PacketState{
ChannelId: channelID,
Sequence: sequence,
Data: data,
}
}
Loading

0 comments on commit cae86e6

Please sign in to comment.