Fix query unrelayed packets logic (#6733)

* add some query functions to channel keeper

* update grpc queries and cli cmd and tests

* rerun proto gen after merging master

* fix build broken from merge conflicts

* add test and update proto

* Update x/ibc/04-channel/client/cli/query.go

Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
colin axner
2020-07-20 12:42:36 +00:00
committed by GitHub
co-authored by Federico Kunze
parent 5c0e3b4de5
commit 1d6344888e
9 changed files with 1089 additions and 288 deletions
+1 -3
View File
@@ -12,9 +12,7 @@ import (
// query routes supported by the IBC client Querier
const (
QueryAllClients = "client_states"
QueryClientState = "client_state"
QueryConsensusState = "consensus_state"
QueryAllClients = "client_states"
)
// QueryAllClientsParams defines the parameters necessary for querying for all
-7
View File
@@ -7,13 +7,6 @@ import (
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
)
// query routes supported by the IBC connection Querier
const (
QueryAllConnections = "connections"
QueryClientConnections = "client_connections"
QueryAllClientConnections = "all_client_connections"
)
// NewQueryConnectionResponse creates a new QueryConnectionResponse instance
func NewQueryConnectionResponse(
connection ConnectionEnd, proof []byte, height int64,
+24 -21
View File
@@ -16,7 +16,10 @@ import (
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
)
const flagSequences = "sequences"
const (
flagSequences = "sequences"
flagAcknowledgements = "acknowledgements"
)
// GetCmdQueryChannels defines the command to query all the channels ends
// that this chain mantains.
@@ -265,18 +268,21 @@ func GetCmdQueryPacketCommitment() *cobra.Command {
return cmd
}
// GetCmdQueryUnrelayedPackets defines the command to query all the unrelayed packets.
// GetCmdQueryUnrelayedPackets defines the command to query all the unrelayed
// packets for either packet commitments or acknowledgements.
func GetCmdQueryUnrelayedPackets() *cobra.Command {
cmd := &cobra.Command{
Use: "unrelayed-packets [port-id] [channel-id]",
Short: "Query all the unrelayed packets associated with a channel",
Long: `It indicates if a packet, given a list of packet commitment sequences, is unrelayed.
An unrelayed packet corresponds to:
Long: `Determine if a packet, given a list of packet commitment sequences, is unrelayed.
- Unrelayed packet commitments: when no acknowledgement exists for the given sequence.
- Unrelayed packet acknowledgements: when an acknowledgement exists and a packet commitment also exists.`,
Example: fmt.Sprintf("%s query %s %s unrelayed-packets [port-id] [channel-id] --sequences=1,2,3", version.AppName, host.ModuleName, types.SubModuleName),
Args: cobra.ExactArgs(2),
If the '-acknowledgements' flag is false (default) then the return value represents:
- Unrelayed packet commitments: no acknowledgement exists for the given packet commitment sequence.
Otherwise, the return value represents:
- Unrelayed packet acknowledgements: an acknowledgement exists for the given packet commitment sequence.`,
Example: fmt.Sprintf("%s query %s %s unrelayed-packets [port-id] [channel-id] --sequences=1,2,3 --acknowledgements=false", version.AppName, host.ModuleName, types.SubModuleName),
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)
clientCtx, err := client.ReadQueryCommandFlags(clientCtx, cmd.Flags())
@@ -290,22 +296,21 @@ An unrelayed packet corresponds to:
return err
}
acknowledgements, err := cmd.Flags().GetBool(flagAcknowledgements)
if err != nil {
return err
}
seqs := make([]uint64, len(seqSlice))
for i := range seqSlice {
seqs[i] = uint64(seqSlice[i])
}
offset, _ := cmd.Flags().GetInt(flags.FlagPage)
limit, _ := cmd.Flags().GetInt(flags.FlagLimit)
req := &types.QueryUnrelayedPacketsRequest{
PortID: args[0],
ChannelID: args[1],
Sequences: seqs,
Req: &query.PageRequest{
Offset: uint64(offset),
Limit: uint64(limit),
},
PortID: args[0],
ChannelID: args[1],
PacketCommitmentSequences: seqs,
Acknowledgements: acknowledgements,
}
res, err := queryClient.UnrelayedPackets(context.Background(), req)
@@ -313,14 +318,12 @@ An unrelayed packet corresponds to:
return err
}
clientCtx = clientCtx.WithHeight(res.Height)
return clientCtx.PrintOutput(res)
},
}
cmd.Flags().Int64Slice(flagSequences, []int64{}, "comma separated list of packet sequence numbers")
cmd.Flags().Int(flags.FlagPage, 1, "pagination page of light clients to to query for")
cmd.Flags().Int(flags.FlagLimit, 100, "pagination limit of light clients to query for")
cmd.Flags().Bool(flagAcknowledgements, false, "boolean indicating if unrelayed acknowledgements (true) or unrelayed packet commitments (false) are returned.")
flags.AddQueryFlagsToCmd(cmd)
return cmd
+42 -20
View File
@@ -189,7 +189,40 @@ func (q Keeper) PacketCommitments(c context.Context, req *types.QueryPacketCommi
}, nil
}
// UnrelayedPackets implements the Query/UnrelayedPackets gRPC method
// PacketAcknowledgement implements the Query/PacketAcknowledgement gRPC method
func (q Keeper) PacketAcknowledgement(c context.Context, req *types.QueryPacketAcknowledgementRequest) (*types.QueryPacketAcknowledgementResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
if err := validategRPCRequest(req.PortID, req.ChannelID); err != nil {
return nil, err
}
if req.Sequence == 0 {
return nil, status.Error(codes.InvalidArgument, "packet sequence cannot be 0")
}
ctx := sdk.UnwrapSDKContext(c)
acknowledgementBz, found := q.GetPacketAcknowledgement(ctx, req.PortID, req.ChannelID, req.Sequence)
if !found || len(acknowledgementBz) == 0 {
return nil, status.Error(codes.NotFound, "packet acknowledgement hash not found")
}
return types.NewQueryPacketAcknowledgementResponse(req.PortID, req.ChannelID, req.Sequence, acknowledgementBz, nil, ctx.BlockHeight()), nil
}
// UnrelayedPackets implements the Query/UnrelayedPackets gRPC method. Given
// a list of counterparty packet commitments, the querier checks if the packet
// sequence has an acknowledgement stored. If req.Acknowledgements is true then
// all unrelayed acknowledgements are returned (ack exists), otherwise all
// unrelayed packet commitments are returned (ack does not exist).
//
// NOTE: The querier makes the assumption that the provided list of packet
// commitments is correct and will not function properly if the list
// is not up to date. Ideally the query height should equal the latest height
// on the counterparty's client which represents this chain.
func (q Keeper) UnrelayedPackets(c context.Context, req *types.QueryUnrelayedPacketsRequest) (*types.QueryUnrelayedPacketsResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
@@ -201,34 +234,23 @@ func (q Keeper) UnrelayedPackets(c context.Context, req *types.QueryUnrelayedPac
ctx := sdk.UnwrapSDKContext(c)
var (
unrelayedPackets = []uint64{}
store sdk.KVStore
res *query.PageResponse
err error
)
var unrelayedSequences = []uint64{}
for i, seq := range req.Sequences {
for i, seq := range req.PacketCommitmentSequences {
if seq == 0 {
return nil, status.Errorf(codes.InvalidArgument, "packet sequence %d cannot be 0", i)
}
store = prefix.NewStore(ctx.KVStore(q.storeKey), host.KeyPacketAcknowledgement(req.PortID, req.ChannelID, seq))
res, err = query.Paginate(store, req.Req, func(_, _ []byte) error {
return nil
})
if err != nil {
// ignore error and continue to the next sequence item
continue
// if req.Acknowledgements is true append sequences with an existing acknowledgement
// otherwise append sequences without an existing acknowledgement.
if _, found := q.GetPacketAcknowledgement(ctx, req.PortID, req.ChannelID, seq); found == req.Acknowledgements {
unrelayedSequences = append(unrelayedSequences, seq)
}
unrelayedPackets = append(unrelayedPackets, seq)
}
return &types.QueryUnrelayedPacketsResponse{
Packets: unrelayedPackets,
Res: res,
Height: ctx.BlockHeight(),
Sequences: unrelayedSequences,
Height: ctx.BlockHeight(),
}, nil
}
+216 -13
View File
@@ -481,6 +481,103 @@ func (suite *KeeperTestSuite) TestQueryPacketCommitments() {
}
}
func (suite *KeeperTestSuite) TestQueryPacketAcknowledgement() {
var (
req *types.QueryPacketAcknowledgementRequest
expAck []byte
)
testCases := []struct {
msg string
malleate func()
expPass bool
}{
{
"empty request",
func() {
req = nil
},
false,
},
{
"invalid port ID",
func() {
req = &types.QueryPacketAcknowledgementRequest{
PortID: "",
ChannelID: "test-channel-id",
Sequence: 0,
}
},
false,
},
{
"invalid channel ID",
func() {
req = &types.QueryPacketAcknowledgementRequest{
PortID: "test-port-id",
ChannelID: "",
Sequence: 0,
}
},
false,
},
{"invalid sequence",
func() {
req = &types.QueryPacketAcknowledgementRequest{
PortID: "test-port-id",
ChannelID: "test-channel-id",
Sequence: 0,
}
},
false,
},
{"channel not found",
func() {
req = &types.QueryPacketAcknowledgementRequest{
PortID: "test-port-id",
ChannelID: "test-channel-id",
Sequence: 1,
}
},
false,
},
{
"success",
func() {
_, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB)
expAck = []byte("hash")
suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), channelA.PortID, channelA.ID, 1, expAck)
req = &types.QueryPacketAcknowledgementRequest{
PortID: channelA.PortID,
ChannelID: channelA.ID,
Sequence: 1,
}
},
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
suite.SetupTest() // reset
tc.malleate()
ctx := sdk.WrapSDKContext(suite.chainA.GetContext())
res, err := suite.chainA.QueryServer.PacketAcknowledgement(ctx, req)
if tc.expPass {
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(expAck, res.Acknowledgement)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *KeeperTestSuite) TestQueryUnrelayedPackets() {
var (
req *types.QueryUnrelayedPacketsRequest
@@ -523,30 +620,136 @@ func (suite *KeeperTestSuite) TestQueryUnrelayedPackets() {
"invalid seq",
func() {
req = &types.QueryUnrelayedPacketsRequest{
PortID: "test-port-id",
ChannelID: "test-channel-id",
Sequences: []uint64{0},
PortID: "test-port-id",
ChannelID: "test-channel-id",
PacketCommitmentSequences: []uint64{0},
}
},
false,
},
{
"success",
"basic success unrelayed packet commitments",
func() {
_, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB)
// no ack exists
expSeq = []uint64{1}
req = &types.QueryUnrelayedPacketsRequest{
PortID: channelA.PortID,
ChannelID: channelA.ID,
PacketCommitmentSequences: []uint64{1},
Acknowledgements: false,
}
},
true,
},
{
"basic success unrelayed packet commitments, nothing to relay",
func() {
_, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB)
// ack exists
ack := types.NewPacketAckCommitment(channelA.PortID, channelA.ID, 1, []byte("hash"))
suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), channelA.PortID, channelA.ID, 1, ack.Hash)
expSeq = []uint64{}
req = &types.QueryUnrelayedPacketsRequest{
PortID: channelA.PortID,
ChannelID: channelA.ID,
PacketCommitmentSequences: []uint64{1},
Acknowledgements: false,
}
},
true,
},
{
"basic success unrelayed acknowledgements",
func() {
_, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB)
// ack exists
ack := types.NewPacketAckCommitment(channelA.PortID, channelA.ID, 1, []byte("hash"))
suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), channelA.PortID, channelA.ID, 1, ack.Hash)
expSeq = []uint64{1}
req = &types.QueryUnrelayedPacketsRequest{
PortID: channelA.PortID,
ChannelID: channelA.ID,
Sequences: []uint64{1},
Req: &query.PageRequest{
Key: nil,
Limit: 1,
CountTotal: false,
},
PortID: channelA.PortID,
ChannelID: channelA.ID,
PacketCommitmentSequences: []uint64{1},
Acknowledgements: true,
}
},
true,
},
{
"basic success unrelayed acknowledgements, nothing to relay",
func() {
_, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB)
// no ack exists
expSeq = []uint64{}
req = &types.QueryUnrelayedPacketsRequest{
PortID: channelA.PortID,
ChannelID: channelA.ID,
PacketCommitmentSequences: []uint64{1},
Acknowledgements: true,
}
},
true,
},
{
"success multiple unrelayed packet commitments",
func() {
_, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB)
expSeq = []uint64{} // reset
packetCommitments := []uint64{}
// set ack for every other sequence
for seq := uint64(1); seq < 10; seq++ {
packetCommitments = append(packetCommitments, seq)
if seq%2 == 0 {
ack := types.NewPacketAckCommitment(channelA.PortID, channelA.ID, seq, []byte("hash"))
suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), channelA.PortID, channelA.ID, seq, ack.Hash)
} else {
expSeq = append(expSeq, seq)
}
}
req = &types.QueryUnrelayedPacketsRequest{
PortID: channelA.PortID,
ChannelID: channelA.ID,
PacketCommitmentSequences: packetCommitments,
Acknowledgements: false,
}
},
true,
},
{
"success multiple unrelayed acknowledgements",
func() {
_, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB)
expSeq = []uint64{} // reset
packetCommitments := []uint64{}
// set ack for every other sequence
for seq := uint64(1); seq < 10; seq++ {
packetCommitments = append(packetCommitments, seq)
if seq%2 == 0 {
ack := types.NewPacketAckCommitment(channelA.PortID, channelA.ID, seq, []byte("hash"))
suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), channelA.PortID, channelA.ID, seq, ack.Hash)
expSeq = append(expSeq, seq)
}
}
req = &types.QueryUnrelayedPacketsRequest{
PortID: channelA.PortID,
ChannelID: channelA.ID,
PacketCommitmentSequences: packetCommitments,
Acknowledgements: true,
}
},
true,
@@ -565,7 +768,7 @@ func (suite *KeeperTestSuite) TestQueryUnrelayedPackets() {
if tc.expPass {
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(expSeq, res.Packets)
suite.Require().Equal(expSeq, res.Sequences)
} else {
suite.Require().Error(err)
}
+15 -8
View File
@@ -9,14 +9,8 @@ import (
// query routes supported by the IBC channel Querier
const (
QueryAllChannels = "channels"
QueryChannel = "channel"
QueryConnectionChannels = "connection-channels"
QueryChannelClientState = "channel-client-state"
QueryChannelConsensusState = "channel-consensus-state"
QueryPacketCommitments = "packet-commitments"
QueryUnrelayedAcknowledgements = "unrelayed-acknowledgements"
QueryUnrelayedPacketSends = "unrelayed-packet-sends"
QueryChannelClientState = "channel-client-state"
QueryChannelConsensusState = "channel-consensus-state"
)
// NewQueryChannelResponse creates a new QueryChannelResponse instance
@@ -43,6 +37,19 @@ func NewQueryPacketCommitmentResponse(
}
}
// NewQueryPacketAcknowledgementResponse creates a new QueryPacketAcknowledgementResponse instance
func NewQueryPacketAcknowledgementResponse(
portID, channelID string, sequence uint64, acknowledgement []byte, proof []byte, height int64,
) *QueryPacketAcknowledgementResponse {
path := commitmenttypes.NewMerklePath(strings.Split(host.PacketAcknowledgementPath(portID, channelID, sequence), "/"))
return &QueryPacketAcknowledgementResponse{
Acknowledgement: acknowledgement,
Proof: proof,
ProofPath: path.Pretty(),
ProofHeight: uint64(height),
}
}
// NewQueryNextSequenceReceiveResponse creates a new QueryNextSequenceReceiveResponse instance
func NewQueryNextSequenceReceiveResponse(
portID, channelID string, sequence uint64, proof []byte, height int64,
File diff suppressed because it is too large Load Diff
+5
View File
@@ -47,6 +47,11 @@ func (q Keeper) PacketCommitments(c context.Context, req *channeltypes.QueryPack
return q.ChannelKeeper.PacketCommitments(c, req)
}
// PacketAcknowledgement implements the IBC QueryServer interface
func (q Keeper) PacketAcknowledgement(c context.Context, req *channeltypes.QueryPacketAcknowledgementRequest) (*channeltypes.QueryPacketAcknowledgementResponse, error) {
return q.ChannelKeeper.PacketAcknowledgement(c, req)
}
// UnrelayedPackets implements the IBC QueryServer interface
func (q Keeper) UnrelayedPackets(c context.Context, req *channeltypes.QueryUnrelayedPacketsRequest) (*channeltypes.QueryUnrelayedPacketsResponse, error) {
return q.ChannelKeeper.UnrelayedPackets(c, req)