Skip to content

Commit

Permalink
chore: add packet acknowledgements rpc
Browse files Browse the repository at this point in the history
  • Loading branch information
DimitrisJim committed Dec 2, 2024
1 parent f99629d commit b7bd5e9
Show file tree
Hide file tree
Showing 7 changed files with 1,133 additions and 121 deletions.
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",
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

0 comments on commit b7bd5e9

Please sign in to comment.