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

chore: add packet acknowledgements rpc #7558

Merged
merged 2 commits into from
Dec 2, 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
65 changes: 63 additions & 2 deletions modules/core/04-channel/v2/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
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 @@ -113,7 +112,7 @@ func (q *queryServer) PacketCommitments(ctx context.Context, req *types.QueryPac
}

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

pageRes, err := query.Paginate(store, req.Pagination, func(key, value []byte) error {
keySplit := strings.Split(string(key), "/")
Expand Down Expand Up @@ -165,6 +164,68 @@ func (q *queryServer) PacketAcknowledgement(ctx context.Context, req *types.Quer
return types.NewQueryPacketAcknowledgementResponse(acknowledgement, nil, clienttypes.GetSelfHeight(ctx)), nil
}

// PacketAcknowledgements implements the Query/PacketAcknowledgements gRPC method.
func (q *queryServer) PacketAcknowledgements(ctx context.Context, req *types.QueryPacketAcknowledgementsRequest) (*types.QueryPacketAcknowledgementsResponse, 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.Wrapf(types.ErrChannelNotFound, req.ChannelId).Error())
}

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

// if a list of packet sequences is provided then query for each specific ack and return a list <= len(req.PacketCommitmentSequences)
// otherwise, maintain previous behaviour and perform paginated query
for _, seq := range req.PacketCommitmentSequences {
acknowledgement := q.GetPacketAcknowledgement(ctx, req.ChannelId, seq)
if len(acknowledgement) == 0 {
continue
}

ack := types.NewPacketState(req.ChannelId, seq, acknowledgement)
acks = append(acks, &ack)
}

if len(req.PacketCommitmentSequences) > 0 {
selfHeight := clienttypes.GetSelfHeight(ctx)
return &types.QueryPacketAcknowledgementsResponse{
Acknowledgements: acks,
Pagination: nil,
Height: selfHeight,
}, nil
}

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
}

ack := types.NewPacketState(req.ChannelId, sequence, value)
acks = append(acks, &ack)

return nil
})
if err != nil {
return nil, err
}

return &types.QueryPacketAcknowledgementsResponse{
Acknowledgements: acks,
Pagination: pageRes,
Height: clienttypes.GetSelfHeight(ctx),
}, nil
}

// PacketReceipt implements the Query/PacketReceipt gRPC method.
func (q *queryServer) PacketReceipt(ctx context.Context, req *types.QueryPacketReceiptRequest) (*types.QueryPacketReceiptResponse, error) {
if req == nil {
Expand Down
113 changes: 113 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 @@ -410,6 +410,119 @@ func (suite *KeeperTestSuite) TestQueryPacketAcknowledgement() {
}
}

func (suite *KeeperTestSuite) TestQueryPacketAcknowledgements() {
var (
req *types.QueryPacketAcknowledgementsRequest
expAcknowledgements = []*types.PacketState{}
)

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

var commitments []uint64

for i := uint64(0); i < 100; i++ {
ack := types.NewPacketState(path.EndpointA.ChannelID, i, []byte(fmt.Sprintf("hash_%d", i)))
suite.chainA.App.GetIBCKeeper().ChannelKeeperV2.SetPacketAcknowledgement(suite.chainA.GetContext(), ack.ChannelId, ack.Sequence, ack.Data)

if i < 10 { // populate the store with 100 and query for 10 specific acks
expAcknowledgements = append(expAcknowledgements, &ack)
commitments = append(commitments, ack.Sequence)
}
}

req = &types.QueryPacketAcknowledgementsRequest{
ChannelId: path.EndpointA.ChannelID,
PacketCommitmentSequences: commitments,
Pagination: nil,
}
},
nil,
},
{
"success: with pagination",
func() {
path := ibctesting.NewPath(suite.chainA, suite.chainB)
path.SetupV2()

expAcknowledgements = make([]*types.PacketState, 0, 10)

for i := uint64(1); i <= 10; i++ {
ack := types.NewPacketState(path.EndpointA.ChannelID, i, []byte(fmt.Sprintf("hash_%d", i)))
suite.chainA.App.GetIBCKeeper().ChannelKeeperV2.SetPacketAcknowledgement(suite.chainA.GetContext(), ack.ChannelId, ack.Sequence, ack.Data)
expAcknowledgements = append(expAcknowledgements, &ack)
}

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

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.App.GetIBCKeeper().ChannelKeeperV2)
res, err := queryServer.PacketAcknowledgements(ctx, req)

expPass := tc.expError == nil
if expPass {
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(expAcknowledgements, res.Acknowledgements)
} else {
suite.Require().ErrorIs(err, tc.expError)
}
})
}
}

func (suite *KeeperTestSuite) TestQueryPacketReceipt() {
var (
expReceipt bool
Expand Down
10 changes: 10 additions & 0 deletions modules/core/04-channel/v2/types/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,13 @@ const (
// the creator key is not a part of the ics-24 host specification
CreatorKey = "creator"
)

// PacketCommitmentPrefixKey returns the store key prefix under which packet commitments for a particular channel are stored.
func PacketCommitmentPrefixKey(channelID string) []byte {
return append([]byte(channelID), byte(1))
}

// PacketAcknowledgementPrefixKey returns the store key prefix under which packet acknowledgements for a particular channel are stored.
func PacketAcknowledgementPrefixKey(channelID string) []byte {
return append([]byte(channelID), byte(3))
}
Loading
Loading