Skip to content

Commit 2c0f498

Browse files
committed
chore: add unreceived acks rpc
1 parent 831a351 commit 2c0f498

File tree

6 files changed

+921
-57
lines changed

6 files changed

+921
-57
lines changed

modules/core/04-channel/v2/client/cli/cli.go

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ func GetQueryCmd() *cobra.Command {
2525
getCmdQueryPacketCommitments(),
2626
getCmdQueryPacketAcknowledgement(),
2727
getCmdQueryPacketReceipt(),
28+
getCmdQueryUnreceivedAcks(),
2829
)
2930

3031
return queryCmd

modules/core/04-channel/v2/client/cli/query.go

+53
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ import (
1414
"github.com/cosmos/ibc-go/v9/modules/core/exported"
1515
)
1616

17+
const (
18+
flagSequences = "sequences"
19+
)
20+
1721
// getCmdQueryChannel defines the command to query the channel information (creator and channel) for the given channel ID.
1822
func getCmdQueryChannel() *cobra.Command {
1923
cmd := &cobra.Command{
@@ -282,3 +286,52 @@ func getCmdQueryPacketReceipt() *cobra.Command {
282286

283287
return cmd
284288
}
289+
290+
// getCmdQueryUnreceivedAcks defines the command to query all the unreceived acks on the original sending chain
291+
func getCmdQueryUnreceivedAcks() *cobra.Command {
292+
cmd := &cobra.Command{
293+
Use: "unreceived-acks [channel-id]",
294+
Short: "Query all the unreceived acks associated with a channel",
295+
Long: `Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain.
296+
297+
The return value represents:
298+
- Unreceived packet acknowledgement: packet commitment exists on original sending (executing) chain and ack exists on receiving chain.
299+
`,
300+
Example: fmt.Sprintf("%s query %s %s unreceived-acks [port-id] [channel-id] --sequences=1,2,3", version.AppName, exported.ModuleName, types.SubModuleName),
301+
Args: cobra.ExactArgs(2),
302+
RunE: func(cmd *cobra.Command, args []string) error {
303+
clientCtx, err := client.GetClientQueryContext(cmd)
304+
if err != nil {
305+
return err
306+
}
307+
queryClient := types.NewQueryClient(clientCtx)
308+
309+
seqSlice, err := cmd.Flags().GetInt64Slice(flagSequences)
310+
if err != nil {
311+
return err
312+
}
313+
314+
seqs := make([]uint64, len(seqSlice))
315+
for i := range seqSlice {
316+
seqs[i] = uint64(seqSlice[i])
317+
}
318+
319+
req := &types.QueryUnreceivedAcksRequest{
320+
ChannelId: args[0],
321+
PacketAckSequences: seqs,
322+
}
323+
324+
res, err := queryClient.UnreceivedAcks(cmd.Context(), req)
325+
if err != nil {
326+
return err
327+
}
328+
329+
return clientCtx.PrintProto(res)
330+
},
331+
}
332+
333+
cmd.Flags().Int64Slice(flagSequences, []int64{}, "comma separated list of packet sequence numbers")
334+
flags.AddQueryFlagsToCmd(cmd)
335+
336+
return cmd
337+
}

modules/core/04-channel/v2/keeper/grpc_query.go

+52
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,55 @@ func (q *queryServer) PacketReceipt(ctx context.Context, req *types.QueryPacketR
187187

188188
return types.NewQueryPacketReceiptResponse(hasReceipt, nil, clienttypes.GetSelfHeight(ctx)), nil
189189
}
190+
191+
// UnreceivedAcks implements the Query/UnreceivedAcks gRPC method. Given
192+
// a list of counterparty packet acknowledgements, the querier checks if the packet
193+
// has already been received by checking if the packet commitment still exists on this
194+
// chain (original sender) for the packet sequence.
195+
// All acknowledgmeents that haven't been received yet are returned in the response.
196+
// Usage: To use this method correctly, first query all packet acknowledgements on
197+
// the original receiving chain (ie the chain that wrote the acks) using the Query/PacketAcknowledgements gRPC method.
198+
// Then input the returned sequences into the QueryUnreceivedAcksRequest
199+
// and send the request to this Query/UnreceivedAcks on the **original sending**
200+
// chain. This gRPC method will then return the list of packet sequences whose
201+
// acknowledgements are already written on the receiving chain but haven't yet
202+
// been received back to the sending chain.
203+
//
204+
// NOTE: The querier makes the assumption that the provided list of packet
205+
// acknowledgements is correct and will not function properly if the list
206+
// is not up to date. Ideally the query height should equal the latest height
207+
// on the counterparty's client which represents this chain.
208+
func (q *queryServer) UnreceivedAcks(ctx context.Context, req *types.QueryUnreceivedAcksRequest) (*types.QueryUnreceivedAcksResponse, error) {
209+
if req == nil {
210+
return nil, status.Error(codes.InvalidArgument, "empty request")
211+
}
212+
213+
if err := host.ChannelIdentifierValidator(req.ChannelId); err != nil {
214+
return nil, status.Error(codes.InvalidArgument, err.Error())
215+
}
216+
217+
if !q.HasChannel(ctx, req.ChannelId) {
218+
return nil, status.Error(codes.NotFound, errorsmod.Wrap(types.ErrChannelNotFound, req.ChannelId).Error())
219+
}
220+
221+
var unreceivedSequences []uint64
222+
223+
for i, seq := range req.PacketAckSequences {
224+
if seq == 0 {
225+
return nil, status.Errorf(codes.InvalidArgument, "packet sequence %d cannot be 0", i)
226+
}
227+
228+
// if packet commitment still exists on the original sending chain, then packet ack has not been received
229+
// since processing the ack will delete the packet commitment
230+
if commitment := q.GetPacketCommitment(ctx, req.ChannelId, seq); len(commitment) != 0 {
231+
unreceivedSequences = append(unreceivedSequences, seq)
232+
}
233+
234+
}
235+
236+
selfHeight := clienttypes.GetSelfHeight(ctx)
237+
return &types.QueryUnreceivedAcksResponse{
238+
Sequences: unreceivedSequences,
239+
Height: selfHeight,
240+
}, nil
241+
}

0 commit comments

Comments
 (0)