diff --git a/x/ibc/04-channel/alias.go b/x/ibc/04-channel/alias.go index 4a8efff613..3afc7365a5 100644 --- a/x/ibc/04-channel/alias.go +++ b/x/ibc/04-channel/alias.go @@ -2,8 +2,8 @@ package channel // autogenerated code using github.com/rigelrozanski/multitool // aliases generated for the following subdirectories: -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/ibc/04-channel/keeper // ALIASGEN: github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/ibc/04-channel/keeper import ( "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/keeper" @@ -11,62 +11,99 @@ import ( ) const ( - SubModuleName = types.SubModuleName - StoreKey = types.StoreKey - RouterKey = types.RouterKey - QuerierRoute = types.QuerierRoute - QueryAllChannels = types.QueryAllChannels - QueryConnectionChannels = types.QueryConnectionChannels - QueryChannel = types.QueryChannel - UNINITIALIZED = types.UNINITIALIZED - INIT = types.INIT - TRYOPEN = types.TRYOPEN - OPEN = types.OPEN - CLOSED = types.CLOSED - NONE = types.NONE - UNORDERED = types.UNORDERED - ORDERED = types.ORDERED + AttributeKeyConnectionID = types.AttributeKeyConnectionID + AttributeKeyPortID = types.AttributeKeyPortID + AttributeKeyChannelID = types.AttributeKeyChannelID + AttributeCounterpartyPortID = types.AttributeCounterpartyPortID + AttributeCounterpartyChannelID = types.AttributeCounterpartyChannelID + EventTypeSendPacket = types.EventTypeSendPacket + EventTypeRecvPacket = types.EventTypeRecvPacket + EventTypeAcknowledgePacket = types.EventTypeAcknowledgePacket + EventTypeCleanupPacket = types.EventTypeCleanupPacket + EventTypeTimeoutPacket = types.EventTypeTimeoutPacket + AttributeKeyData = types.AttributeKeyData + AttributeKeyAck = types.AttributeKeyAck + AttributeKeyTimeoutHeight = types.AttributeKeyTimeoutHeight + AttributeKeyTimeoutTimestamp = types.AttributeKeyTimeoutTimestamp + AttributeKeySequence = types.AttributeKeySequence + AttributeKeySrcPort = types.AttributeKeySrcPort + AttributeKeySrcChannel = types.AttributeKeySrcChannel + AttributeKeyDstPort = types.AttributeKeyDstPort + AttributeKeyDstChannel = types.AttributeKeyDstChannel + SubModuleName = types.SubModuleName + StoreKey = types.StoreKey + RouterKey = types.RouterKey + QuerierRoute = types.QuerierRoute + QueryAllChannels = types.QueryAllChannels + QueryChannel = types.QueryChannel + QueryConnectionChannels = types.QueryConnectionChannels + QueryPacketCommitments = types.QueryPacketCommitments + QueryUnrelayedAcknowledgements = types.QueryUnrelayedAcknowledgements + QueryUnrelayedPacketSends = types.QueryUnrelayedPacketSends + UNINITIALIZED = types.UNINITIALIZED + INIT = types.INIT + TRYOPEN = types.TRYOPEN + OPEN = types.OPEN + CLOSED = types.CLOSED + NONE = types.NONE + UNORDERED = types.UNORDERED + ORDERED = types.ORDERED ) var ( // functions aliases - NewKeeper = keeper.NewKeeper - QuerierChannels = keeper.QuerierChannels - QuerierConnectionChannels = keeper.QuerierConnectionChannels - NewChannel = types.NewChannel - NewCounterparty = types.NewCounterparty - NewIdentifiedChannel = types.NewIdentifiedChannel - RegisterCodec = types.RegisterCodec - RegisterInterfaces = types.RegisterInterfaces - ErrChannelExists = types.ErrChannelExists - ErrChannelNotFound = types.ErrChannelNotFound - ErrInvalidCounterparty = types.ErrInvalidCounterparty - ErrChannelCapabilityNotFound = types.ErrChannelCapabilityNotFound - ErrInvalidPacket = types.ErrInvalidPacket - ErrSequenceSendNotFound = types.ErrSequenceSendNotFound - ErrSequenceReceiveNotFound = types.ErrSequenceReceiveNotFound - ErrPacketTimeout = types.ErrPacketTimeout - ErrInvalidChannel = types.ErrInvalidChannel - ErrInvalidChannelState = types.ErrInvalidChannelState - ErrAcknowledgementTooLong = types.ErrAcknowledgementTooLong - NewMsgChannelOpenInit = types.NewMsgChannelOpenInit - NewMsgChannelOpenTry = types.NewMsgChannelOpenTry - NewMsgChannelOpenAck = types.NewMsgChannelOpenAck - NewMsgChannelOpenConfirm = types.NewMsgChannelOpenConfirm - NewMsgChannelCloseInit = types.NewMsgChannelCloseInit - NewMsgChannelCloseConfirm = types.NewMsgChannelCloseConfirm - NewMsgPacket = types.NewMsgPacket - NewMsgTimeout = types.NewMsgTimeout - NewMsgAcknowledgement = types.NewMsgAcknowledgement - NewPacket = types.NewPacket - NewPacketAckCommitment = types.NewPacketAckCommitment - NewPacketSequence = types.NewPacketSequence - NewChannelResponse = types.NewChannelResponse - DefaultGenesisState = types.DefaultGenesisState - NewGenesisState = types.NewGenesisState + NewChannel = types.NewChannel + NewCounterparty = types.NewCounterparty + NewIdentifiedChannel = types.NewIdentifiedChannel + RegisterCodec = types.RegisterCodec + RegisterInterfaces = types.RegisterInterfaces + NewPacketAckCommitment = types.NewPacketAckCommitment + NewPacketSequence = types.NewPacketSequence + NewGenesisState = types.NewGenesisState + DefaultGenesisState = types.DefaultGenesisState + NewMsgChannelOpenInit = types.NewMsgChannelOpenInit + NewMsgChannelOpenTry = types.NewMsgChannelOpenTry + NewMsgChannelOpenAck = types.NewMsgChannelOpenAck + NewMsgChannelOpenConfirm = types.NewMsgChannelOpenConfirm + NewMsgChannelCloseInit = types.NewMsgChannelCloseInit + NewMsgChannelCloseConfirm = types.NewMsgChannelCloseConfirm + NewMsgPacket = types.NewMsgPacket + NewMsgTimeout = types.NewMsgTimeout + NewMsgAcknowledgement = types.NewMsgAcknowledgement + CommitPacket = types.CommitPacket + CommitAcknowledgement = types.CommitAcknowledgement + NewPacket = types.NewPacket + NewChannelResponse = types.NewChannelResponse + NewQueryAllChannelsParams = types.NewQueryAllChannelsParams + NewQueryConnectionChannelsParams = types.NewQueryConnectionChannelsParams + NewQueryPacketCommitmentsParams = types.NewQueryPacketCommitmentsParams + NewQueryUnrelayedPacketsParams = types.NewQueryUnrelayedPacketsParams + NewPacketResponse = types.NewPacketResponse + NewRecvResponse = types.NewRecvResponse + NewKeeper = keeper.NewKeeper + QuerierChannels = keeper.QuerierChannels + QuerierConnectionChannels = keeper.QuerierConnectionChannels + QuerierPacketCommitments = keeper.QuerierPacketCommitments + QuerierUnrelayedAcknowledgements = keeper.QuerierUnrelayedAcknowledgements + QuerierUnrelayedPacketSends = keeper.QuerierUnrelayedPacketSends // variable aliases SubModuleCdc = types.SubModuleCdc + ErrChannelExists = types.ErrChannelExists + ErrChannelNotFound = types.ErrChannelNotFound + ErrInvalidChannel = types.ErrInvalidChannel + ErrInvalidChannelState = types.ErrInvalidChannelState + ErrInvalidChannelOrdering = types.ErrInvalidChannelOrdering + ErrInvalidCounterparty = types.ErrInvalidCounterparty + ErrInvalidChannelCapability = types.ErrInvalidChannelCapability + ErrChannelCapabilityNotFound = types.ErrChannelCapabilityNotFound + ErrSequenceSendNotFound = types.ErrSequenceSendNotFound + ErrSequenceReceiveNotFound = types.ErrSequenceReceiveNotFound + ErrSequenceAckNotFound = types.ErrSequenceAckNotFound + ErrInvalidPacket = types.ErrInvalidPacket + ErrPacketTimeout = types.ErrPacketTimeout + ErrTooManyConnectionHops = types.ErrTooManyConnectionHops + ErrAcknowledgementTooLong = types.ErrAcknowledgementTooLong EventTypeChannelOpenInit = types.EventTypeChannelOpenInit EventTypeChannelOpenTry = types.EventTypeChannelOpenTry EventTypeChannelOpenAck = types.EventTypeChannelOpenAck @@ -74,31 +111,40 @@ var ( EventTypeChannelCloseInit = types.EventTypeChannelCloseInit EventTypeChannelCloseConfirm = types.EventTypeChannelCloseConfirm AttributeValueCategory = types.AttributeValueCategory + ErrInvalidLengthTypes = types.ErrInvalidLengthTypes + ErrIntOverflowTypes = types.ErrIntOverflowTypes + ErrUnexpectedEndOfGroupTypes = types.ErrUnexpectedEndOfGroupTypes ) // nolint: golint type ( - Keeper = keeper.Keeper - Channel = types.Channel - Counterparty = types.Counterparty - State = types.State - Order = types.Order - IdentifiedChannel = types.IdentifiedChannel - ClientKeeper = types.ClientKeeper - ConnectionKeeper = types.ConnectionKeeper - PortKeeper = types.PortKeeper - MsgChannelOpenInit = types.MsgChannelOpenInit - MsgChannelOpenTry = types.MsgChannelOpenTry - MsgChannelOpenAck = types.MsgChannelOpenAck - MsgChannelOpenConfirm = types.MsgChannelOpenConfirm - MsgChannelCloseInit = types.MsgChannelCloseInit - MsgChannelCloseConfirm = types.MsgChannelCloseConfirm - MsgPacket = types.MsgPacket - MsgAcknowledgement = types.MsgAcknowledgement - MsgTimeout = types.MsgTimeout - Packet = types.Packet - ChannelResponse = types.ChannelResponse - PacketAckCommitment = types.PacketAckCommitment - PacketSequence = types.PacketSequence - GenesisState = types.GenesisState + IdentifiedChannel = types.IdentifiedChannel + ClientKeeper = types.ClientKeeper + ConnectionKeeper = types.ConnectionKeeper + PortKeeper = types.PortKeeper + PacketAckCommitment = types.PacketAckCommitment + PacketSequence = types.PacketSequence + GenesisState = types.GenesisState + ChannelResponse = types.ChannelResponse + QueryAllChannelsParams = types.QueryAllChannelsParams + QueryConnectionChannelsParams = types.QueryConnectionChannelsParams + QueryPacketCommitmentsParams = types.QueryPacketCommitmentsParams + QueryUnrelayedPacketsParams = types.QueryUnrelayedPacketsParams + PacketResponse = types.PacketResponse + RecvResponse = types.RecvResponse + State = types.State + Order = types.Order + MsgChannelOpenInit = types.MsgChannelOpenInit + MsgChannelOpenTry = types.MsgChannelOpenTry + MsgChannelOpenAck = types.MsgChannelOpenAck + MsgChannelOpenConfirm = types.MsgChannelOpenConfirm + MsgChannelCloseInit = types.MsgChannelCloseInit + MsgChannelCloseConfirm = types.MsgChannelCloseConfirm + MsgPacket = types.MsgPacket + MsgTimeout = types.MsgTimeout + MsgAcknowledgement = types.MsgAcknowledgement + Channel = types.Channel + Counterparty = types.Counterparty + Packet = types.Packet + Keeper = keeper.Keeper ) diff --git a/x/ibc/04-channel/keeper/keeper.go b/x/ibc/04-channel/keeper/keeper.go index 6cea0edb34..268a079901 100644 --- a/x/ibc/04-channel/keeper/keeper.go +++ b/x/ibc/04-channel/keeper/keeper.go @@ -230,7 +230,7 @@ func (k Keeper) GetAllPacketAckSeqs(ctx sdk.Context) (seqs []types.PacketSequenc } // IteratePacketCommitment provides an iterator over all PacketCommitment objects. For each -// aknowledgement, cb will be called. If the cb returns true, the iterator will close +// packet commitment, cb will be called. If the cb returns true, the iterator will close // and stop. func (k Keeper) IteratePacketCommitment(ctx sdk.Context, cb func(portID, channelID string, sequence uint64, hash []byte) bool) { store := ctx.KVStore(k.storeKey) @@ -248,6 +248,26 @@ func (k Keeper) GetAllPacketCommitments(ctx sdk.Context) (commitments []types.Pa return commitments } +// IteratePacketCommitmentAtChannel provides an iterator over all PacketCommmitment objects +// at a specified channel. For each packet commitment, cb will be called. If the cb returns +// true, the iterator will close and stop. +func (k Keeper) IteratePacketCommitmentAtChannel(ctx sdk.Context, portID, channelID string, cb func(_, _ string, sequence uint64, hash []byte) bool) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte(host.PacketCommitmentPrefixPath(portID, channelID))) + k.iterateHashes(ctx, iterator, cb) +} + +// GetAllPacketCommitmentsAtChannel returns all stored PacketCommitments objects for a specified +// port ID and channel ID. +func (k Keeper) GetAllPacketCommitmentsAtChannel(ctx sdk.Context, portID, channelID string) (commitments []types.PacketAckCommitment) { + k.IteratePacketCommitmentAtChannel(ctx, portID, channelID, func(_, _ string, sequence uint64, hash []byte) bool { + pc := types.NewPacketAckCommitment(portID, channelID, sequence, hash) + commitments = append(commitments, pc) + return false + }) + return commitments +} + // IteratePacketAcknowledgement provides an iterator over all PacketAcknowledgement objects. For each // aknowledgement, cb will be called. If the cb returns true, the iterator will close // and stop. @@ -306,7 +326,7 @@ func (k Keeper) LookupModuleByChannel(ctx sdk.Context, portID, channelID string) return porttypes.GetModuleOwner(modules), cap, nil } -// common functionality for IteratePacketCommitment and IteratePacketAcknowledgemen +// common functionality for IteratePacketCommitment and IteratePacketAcknowledgement func (k Keeper) iterateHashes(_ sdk.Context, iterator db.Iterator, cb func(portID, channelID string, sequence uint64, hash []byte) bool) { defer iterator.Close() diff --git a/x/ibc/04-channel/keeper/keeper_test.go b/x/ibc/04-channel/keeper/keeper_test.go index 8f50a6fc48..6884595006 100644 --- a/x/ibc/04-channel/keeper/keeper_test.go +++ b/x/ibc/04-channel/keeper/keeper_test.go @@ -18,6 +18,7 @@ import ( ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" + ibckeeper "github.com/cosmos/cosmos-sdk/x/ibc/keeper" "github.com/cosmos/cosmos-sdk/x/staking" ) @@ -51,10 +52,16 @@ const ( disabledTimeoutHeight = 0 ) +var ( + testPacketCommitment = []byte("packet commitment") + testAcknowledgement = []byte("acknowledgement") +) + type KeeperTestSuite struct { suite.Suite - cdc *codec.Codec + cdc *codec.Codec + querier sdk.Querier chainA *TestChain chainB *TestChain @@ -65,6 +72,7 @@ func (suite *KeeperTestSuite) SetupTest() { suite.chainB = NewTestChain(testClientIDB) suite.cdc = suite.chainA.App.Codec() + suite.querier = ibckeeper.NewQuerier(*suite.chainA.App.IBCKeeper) } func (suite *KeeperTestSuite) TestSetChannel() { @@ -200,17 +208,61 @@ func (suite *KeeperTestSuite) TestSetSequence() { suite.Equal(nextSeqAck, storedNextSeqAck) } -func (suite *KeeperTestSuite) TestPackageCommitment() { +// TestPacketCommitment does basic verification of setting and getting of packet commitments within +// the Channel Keeper. +func (suite *KeeperTestSuite) TestPacketCommitment() { ctx := suite.chainB.GetContext() seq := uint64(10) - storedCommitment := suite.chainB.App.IBCKeeper.ChannelKeeper.GetPacketCommitment(ctx, testPort1, testChannel1, seq) - suite.Equal([]byte(nil), storedCommitment) - commitment := []byte("commitment") - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(ctx, testPort1, testChannel1, seq, commitment) + storedCommitment := suite.chainB.App.IBCKeeper.ChannelKeeper.GetPacketCommitment(ctx, testPort1, testChannel1, seq) + suite.Nil(storedCommitment) + + suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(ctx, testPort1, testChannel1, seq, testPacketCommitment) storedCommitment = suite.chainB.App.IBCKeeper.ChannelKeeper.GetPacketCommitment(ctx, testPort1, testChannel1, seq) - suite.Equal(commitment, storedCommitment) + suite.Equal(testPacketCommitment, storedCommitment) +} + +// TestGetAllPacketCommitmentsAtChannel verifies that iterator returns all stored packet commitments +// for a specific channel. +func (suite *KeeperTestSuite) TestGetAllPacketCommitmentsAtChannel() { + // setup + ctx := suite.chainB.GetContext() + expectedSeqs := make(map[uint64]bool) + + seq := uint64(15) + maxSeq := uint64(25) + suite.Require().Greater(maxSeq, seq) + + // create consecutive commitments + for i := uint64(1); i < seq; i++ { + suite.chainB.storePacketCommitment(ctx, testPort1, testChannel1, i) + expectedSeqs[i] = true + } + + // add non-consecutive commitments + for i := seq; i < maxSeq; i += 2 { + suite.chainB.storePacketCommitment(ctx, testPort1, testChannel1, i) + expectedSeqs[i] = true + } + + // add sequence on different channel/port + suite.chainB.storePacketCommitment(ctx, testPort2, testChannel2, maxSeq+1) + + commitments := suite.chainB.App.IBCKeeper.ChannelKeeper.GetAllPacketCommitmentsAtChannel(ctx, testPort1, testChannel1) + + suite.Equal(len(expectedSeqs), len(commitments)) + suite.NotEqual(0, len(commitments)) + + // verify that all the packet commitments were stored + for _, packet := range commitments { + suite.True(expectedSeqs[packet.Sequence]) + suite.Equal(testPort1, packet.PortID) + suite.Equal(testChannel1, packet.ChannelID) + + // prevent duplicates from passing checks + expectedSeqs[packet.Sequence] = false + } } func (suite *KeeperTestSuite) TestSetPacketAcknowledgement() { @@ -442,6 +494,16 @@ func (chain *TestChain) createChannel( return channel } +// storePacketCommitment is a helper function that sets a packet commitment in the Channel Keeper. +func (chain *TestChain) storePacketCommitment(ctx sdk.Context, portID, channelID string, sequence uint64) { + chain.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(ctx, portID, channelID, sequence, testPacketCommitment) +} + +// storeAcknowledgement is a helper function that sets a packet commitment in the Channel Keeper. +func (chain *TestChain) storeAcknowledgement(ctx sdk.Context, portID, channelID string, sequence uint64) { + chain.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(ctx, portID, channelID, sequence, testAcknowledgement) +} + func nextHeader(chain *TestChain) ibctmtypes.Header { return ibctmtypes.CreateTestHeader(chain.Header.SignedHeader.Header.ChainID, int64(chain.Header.GetHeight())+1, chain.Header.Time.Add(time.Minute), chain.Vals, chain.Signers) diff --git a/x/ibc/04-channel/keeper/querier.go b/x/ibc/04-channel/keeper/querier.go index 37982619e1..b11ad08b4f 100644 --- a/x/ibc/04-channel/keeper/querier.go +++ b/x/ibc/04-channel/keeper/querier.go @@ -66,3 +66,79 @@ func QuerierConnectionChannels(ctx sdk.Context, req abci.RequestQuery, k Keeper) return res, nil } + +// QuerierPacketCommitments defines the sdk.Querier to query all packet commitments on a +// specified channel. +func QuerierPacketCommitments(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { + var params types.QueryPacketCommitmentsParams + + if err := k.cdc.UnmarshalJSON(req.Data, ¶ms); err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + + packetCommitments := k.GetAllPacketCommitmentsAtChannel(ctx, params.PortID, params.ChannelID) + sequences := make([]uint64, 0, len(packetCommitments)) + + for _, pc := range packetCommitments { + sequences = append(sequences, pc.Sequence) + } + + start, end := client.Paginate(len(sequences), params.Page, params.Limit, 100) + if start < 0 || end < 0 { + sequences = []uint64{} + } else { + sequences = sequences[start:end] + } + + res, err := codec.MarshalJSONIndent(k.cdc, sequences) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return res, nil +} + +// QuerierUnrelayedAcknowledgements defines the sdk.Querier to query all unrelayed +// acknowledgements for a specified channel end. +func QuerierUnrelayedAcknowledgements(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { + return queryUnrelayedPackets(ctx, req, k, true) +} + +// QuerierUnrelayedPacketSends defines the sdk.Querier to query all unrelayed packets for a +// specified channel end. +func QuerierUnrelayedPacketSends(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { + return queryUnrelayedPackets(ctx, req, k, false) +} + +// helper function to query for unrelayed packets as specified by the isForAcks boolean. If +// set to true it will return unrelayed acknowledgements otherwise it will return unrelayed +// packet sends. +func queryUnrelayedPackets(ctx sdk.Context, req abci.RequestQuery, k Keeper, isForAcks bool) ([]byte, error) { + var params types.QueryUnrelayedPacketsParams + + if err := k.cdc.UnmarshalJSON(req.Data, ¶ms); err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + + var unrelayedPackets []uint64 + for _, seq := range params.Sequences { + if _, found := k.GetPacketAcknowledgement(ctx, params.PortID, params.ChannelID, seq); found == isForAcks { + unrelayedPackets = append(unrelayedPackets, seq) + } + } + + start, end := client.Paginate(len(unrelayedPackets), params.Page, params.Limit, 100) + if start < 0 || end < 0 { + unrelayedPackets = []uint64{} + } else { + unrelayedPackets = unrelayedPackets[start:end] + } + + res, err := codec.MarshalJSONIndent(k.cdc, unrelayedPackets) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return res, nil + +} diff --git a/x/ibc/04-channel/keeper/querier_test.go b/x/ibc/04-channel/keeper/querier_test.go new file mode 100644 index 0000000000..d5f92c2531 --- /dev/null +++ b/x/ibc/04-channel/keeper/querier_test.go @@ -0,0 +1,436 @@ +package keeper_test + +import ( + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/codec" + connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" +) + +// TestQueryChannels tests singular, muliple, and no connection for +// correct retrieval of all channels. +func (suite *KeeperTestSuite) TestQueryChannels() { + path := []string{types.SubModuleName, types.QueryAllChannels} + var ( + expRes []byte + err error + ) + + params := types.NewQueryAllChannelsParams(1, 100) + data, err := suite.cdc.MarshalJSON(params) + suite.NoError(err) + + query := abci.RequestQuery{ + Path: "", + Data: data, + } + + testCases := []struct { + name string + setup func() + }{ + { + "success with different connection channels", + func() { + suite.SetupTest() + channels := make([]types.IdentifiedChannel, 0, 2) + + // create channels on different connections + suite.chainA.createConnection( + testConnectionIDA, testConnectionIDB, + testClientIDA, testClientIDB, + connection.OPEN, + ) + channels = append(channels, + types.NewIdentifiedChannel(testPort1, testChannel1, + suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, + types.OPEN, types.ORDERED, testConnectionIDA, + ), + ), + ) + + suite.chainA.createConnection( + testConnectionIDB, testConnectionIDA, + testClientIDB, testClientIDA, + connection.OPEN, + ) + channels = append(channels, + types.NewIdentifiedChannel(testPort2, testChannel2, + suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, + types.OPEN, types.ORDERED, testConnectionIDB, + ), + ), + ) + + // set expected result + expRes, err = codec.MarshalJSONIndent(suite.cdc, channels) + suite.NoError(err) + }, + }, + { + "success with singular connection channels", + func() { + suite.SetupTest() + channels := make([]types.IdentifiedChannel, 0, 2) + + // create channels on singular connections + suite.chainA.createConnection( + testConnectionIDA, testConnectionIDB, + testClientIDA, testClientIDB, + connection.OPEN, + ) + + channels = append(channels, + types.NewIdentifiedChannel(testPort1, testChannel1, + suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, + types.OPEN, types.ORDERED, testConnectionIDA, + ), + ), + ) + channels = append(channels, + types.NewIdentifiedChannel(testPort2, testChannel2, + suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, + types.OPEN, types.UNORDERED, testConnectionIDA, + ), + ), + ) + + // set expected result + expRes, err = codec.MarshalJSONIndent(suite.cdc, channels) + suite.NoError(err) + }, + }, + { + "success no channels", + func() { + suite.SetupTest() + expRes, err = codec.MarshalJSONIndent(suite.cdc, []types.IdentifiedChannel{}) + suite.NoError(err) + }, + }, + } + + for i, tc := range testCases { + tc.setup() + + bz, err := suite.querier(suite.chainA.GetContext(), path, query) + + suite.NoError(err, "test case %d failed: %s", i, tc.name) + suite.Equal(expRes, bz, "test case %d failed: %s", i, tc.name) + } +} + +// TestQueryConnectionChannel tests querying existing channels on a singular connection. +func (suite *KeeperTestSuite) TestQueryConnectionChannels() { + path := []string{types.SubModuleName, types.QueryConnectionChannels} + + var ( + expRes []byte + err error + ) + + params := types.NewQueryConnectionChannelsParams(testConnectionIDA, 1, 100) + data, err := suite.cdc.MarshalJSON(params) + suite.NoError(err) + + query := abci.RequestQuery{ + Path: "", + Data: data, + } + + testCases := []struct { + name string + setup func() + }{ + { + "success with singular connection channels", + func() { + suite.SetupTest() + channels := make([]types.IdentifiedChannel, 0, 2) + + // create channels on singular connections + suite.chainA.createConnection( + testConnectionIDA, testConnectionIDB, + testClientIDA, testClientIDB, + connection.OPEN, + ) + + channels = append(channels, + types.NewIdentifiedChannel(testPort1, testChannel1, + suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, + types.OPEN, types.ORDERED, testConnectionIDA, + ), + ), + ) + channels = append(channels, + types.NewIdentifiedChannel(testPort2, testChannel2, + suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, + types.OPEN, types.UNORDERED, testConnectionIDA, + ), + ), + ) + + // set expected result + expRes, err = codec.MarshalJSONIndent(suite.cdc, channels) + suite.NoError(err) + }, + }, + { + "success multiple connection channels", + func() { + suite.SetupTest() + channels := make([]types.IdentifiedChannel, 0, 1) + + // create channels on different connections + suite.chainA.createConnection( + testConnectionIDA, testConnectionIDB, + testClientIDA, testClientIDB, + connection.OPEN, + ) + channels = append(channels, + types.NewIdentifiedChannel(testPort1, testChannel1, + suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, + types.OPEN, types.ORDERED, testConnectionIDA, + ), + ), + ) + + suite.chainA.createConnection( + testConnectionIDB, testConnectionIDA, + testClientIDB, testClientIDA, + connection.OPEN, + ) + suite.chainA.createChannel( + testPort2, testChannel2, testPort1, testChannel1, + types.OPEN, types.ORDERED, testConnectionIDB, + ) + + // set expected result + expRes, err = codec.MarshalJSONIndent(suite.cdc, channels) + suite.NoError(err) + }, + }, + { + "success no channels", + func() { + suite.SetupTest() + expRes, err = codec.MarshalJSONIndent(suite.cdc, []types.IdentifiedChannel{}) + suite.NoError(err) + }, + }, + } + + for i, tc := range testCases { + tc.setup() + + bz, err := suite.querier(suite.chainA.GetContext(), path, query) + + suite.NoError(err, "test case %d failed: %s", i, tc.name) + suite.Equal(expRes, bz, "test case %d failed: %s", i, tc.name) + } + +} + +// TestQueryPacketCommitments tests querying packet commitments on a specified channel end. +func (suite *KeeperTestSuite) TestQueryPacketCommitments() { + path := []string{types.SubModuleName, types.QueryPacketCommitments} + + var ( + expRes []byte + ) + + params := types.NewQueryPacketCommitmentsParams(testPort1, testChannel1, 1, 100) + data, err := suite.cdc.MarshalJSON(params) + suite.NoError(err) + + query := abci.RequestQuery{ + Path: "", + Data: data, + } + + testCases := []struct { + name string + setup func() + }{ + { + "success", + func() { + suite.SetupTest() + ctx := suite.chainA.GetContext() + seq := uint64(1) + commitments := []uint64{} + + // create several commitments on the same channel and port + for i := seq; i < 10; i++ { + suite.chainA.storePacketCommitment(ctx, testPort1, testChannel1, i) + commitments = append(commitments, i) + } + + expRes, err = codec.MarshalJSONIndent(suite.cdc, commitments) + suite.NoError(err) + }, + }, + { + "success with multiple channels", + func() { + suite.SetupTest() + ctx := suite.chainA.GetContext() + seq := uint64(1) + commitments := []uint64{} + + // create several commitments on the same channel and port + for i := seq; i < 10; i++ { + suite.chainA.storePacketCommitment(ctx, testPort1, testChannel1, i) + commitments = append(commitments, i) + } + + // create several commitments on a different channel and port + for i := seq; i < 10; i++ { + suite.chainA.storePacketCommitment(ctx, testPort2, testChannel2, i) + } + + expRes, err = codec.MarshalJSONIndent(suite.cdc, commitments) + suite.NoError(err) + }, + }, + { + "success no packet commitments", + func() { + suite.SetupTest() + expRes, err = codec.MarshalJSONIndent(suite.cdc, []uint64{}) + suite.NoError(err) + }, + }, + } + + for i, tc := range testCases { + tc.setup() + + bz, err := suite.querier(suite.chainA.GetContext(), path, query) + + suite.NoError(err, "test case %d failed: %s", i, tc.name) + suite.Equal(expRes, bz, "test case %d failed: %s", i, tc.name) + } + +} + +// TestQueryUnrelayedPackets tests querying unrelayed acknowledgements and unrelayed packets sends +// on a specified channel end. +func (suite *KeeperTestSuite) TestQueryUnrelayedAcks() { + pathAck := []string{types.SubModuleName, types.QueryUnrelayedAcknowledgements} + pathSend := []string{types.SubModuleName, types.QueryUnrelayedPacketSends} + sequences := []uint64{1, 2, 3, 4, 5} + + var ( + expResAck []byte + expResSend []byte + ) + + params := types.NewQueryUnrelayedPacketsParams(testPort1, testChannel1, sequences, 1, 100) + data, err := suite.cdc.MarshalJSON(params) + suite.NoError(err) + + query := abci.RequestQuery{ + Path: "", + Data: data, + } + + testCases := []struct { + name string + setup func() + }{ + { + "success", + func() { + suite.SetupTest() + ctx := suite.chainA.GetContext() + unrelayedAcks := []uint64{} + unrelayedSends := []uint64{} + + // create acknowledgements for first 3 sequences + for _, seq := range sequences { + if seq < 4 { + suite.chainA.storeAcknowledgement(ctx, testPort1, testChannel1, seq) + unrelayedAcks = append(unrelayedAcks, seq) + } else { + unrelayedSends = append(unrelayedSends, seq) + } + } + + expResAck, err = codec.MarshalJSONIndent(suite.cdc, unrelayedAcks) + suite.NoError(err) + + expResSend, err = codec.MarshalJSONIndent(suite.cdc, unrelayedSends) + suite.NoError(err) + + }, + }, + { + "success with multiple channels", + func() { + suite.SetupTest() + ctx := suite.chainA.GetContext() + unrelayedAcks := []uint64{} + unrelayedSends := []uint64{} + + // create acknowledgements for first 3 sequences + for _, seq := range sequences { + if seq < 4 { + suite.chainA.storeAcknowledgement(ctx, testPort1, testChannel1, seq) + unrelayedAcks = append(unrelayedAcks, seq) + } else { + unrelayedSends = append(unrelayedSends, seq) + } + } + + // create acknowledgements for other sequences on different channel/port + for _, seq := range sequences { + if seq >= 4 { + suite.chainA.storeAcknowledgement(ctx, testPort2, testChannel2, seq) + } + } + + expResAck, err = codec.MarshalJSONIndent(suite.cdc, unrelayedAcks) + suite.NoError(err) + + expResSend, err = codec.MarshalJSONIndent(suite.cdc, unrelayedSends) + suite.NoError(err) + }, + }, + { + "success no unrelayed acks", + func() { + suite.SetupTest() + ctx := suite.chainA.GetContext() + + // create acknowledgements for all sequences + for _, seq := range sequences { + suite.chainA.storeAcknowledgement(ctx, testPort1, testChannel1, seq) + } + + expResSend, err = codec.MarshalJSONIndent(suite.cdc, []uint64{}) + suite.NoError(err) + + expResAck, err = codec.MarshalJSONIndent(suite.cdc, sequences) + suite.NoError(err) + }, + }, + } + + for i, tc := range testCases { + tc.setup() + + bz, err := suite.querier(suite.chainA.GetContext(), pathAck, query) + + suite.NoError(err, "test case %d failed: %s", i, tc.name) + suite.Equal(expResAck, bz, "test case %d failed: %s", i, tc.name) + + bz, err = suite.querier(suite.chainA.GetContext(), pathSend, query) + + suite.NoError(err, "test case %d failed: %s", i, tc.name) + suite.Equal(expResSend, bz, "test case %d failed: %s", i, tc.name) + + } + +} diff --git a/x/ibc/04-channel/types/querier.go b/x/ibc/04-channel/types/querier.go index 77afc0c608..0f0db02b7c 100644 --- a/x/ibc/04-channel/types/querier.go +++ b/x/ibc/04-channel/types/querier.go @@ -11,9 +11,12 @@ import ( // query routes supported by the IBC channel Querier const ( - QueryAllChannels = "channels" - QueryChannel = "channel" - QueryConnectionChannels = "connection-channels" + QueryAllChannels = "channels" + QueryChannel = "channel" + QueryConnectionChannels = "connection-channels" + QueryPacketCommitments = "packet-commitments" + QueryUnrelayedAcknowledgements = "unrelayed-acknowledgements" + QueryUnrelayedPacketSends = "unrelayed-packet-sends" ) // ChannelResponse defines the client query response for a channel which also @@ -70,6 +73,46 @@ func NewQueryConnectionChannelsParams(connection string, page, limit int) QueryC } } +// QueryPacketCommitmentsParams defines the parameters necessary for querying +// all packet commitments at an associated port ID and channel ID. +type QueryPacketCommitmentsParams struct { + PortID string `json:"port_id" yaml:"port_id"` + ChannelID string `json:"channel_id" yaml:"channel_id"` + Page int `json:"page" yaml:"page"` + Limit int `json:"limit" yaml:"limit"` +} + +// NewQueryPacketCommitmentsParams creates a new QueryPacketCommitmentsParams instance. +func NewQueryPacketCommitmentsParams(portID, channelID string, page, limit int) QueryPacketCommitmentsParams { + return QueryPacketCommitmentsParams{ + PortID: portID, + ChannelID: channelID, + Page: page, + Limit: limit, + } +} + +// QueryUnrelayedPacketsParams defines the parameters necessary for querying +// unrelayed packets at an associated port ID and channel ID. +type QueryUnrelayedPacketsParams struct { + PortID string `json:"port_id" yaml:"port_id"` + ChannelID string `json:"channel_id" yaml:"channel_id"` + Sequences []uint64 `json:"sequences" yaml:"sequences"` + Page int `json:"page" yaml:"page"` + Limit int `json:"limit" yaml:"limit"` +} + +// NewQueryUnrealyedPacketsParams creates a new QueryUnrelayedPacketsParams instance. +func NewQueryUnrelayedPacketsParams(portID, channelID string, sequences []uint64, page, limit int) QueryUnrelayedPacketsParams { + return QueryUnrelayedPacketsParams{ + PortID: portID, + ChannelID: channelID, + Sequences: sequences, + Page: page, + Limit: limit, + } +} + // PacketResponse defines the client query response for a packet which also // includes a proof, its path and the height form which the proof was retrieved type PacketResponse struct { diff --git a/x/ibc/24-host/keys.go b/x/ibc/24-host/keys.go index 7d2757f47f..94de1182a0 100644 --- a/x/ibc/24-host/keys.go +++ b/x/ibc/24-host/keys.go @@ -139,6 +139,11 @@ func PacketCommitmentPath(portID, channelID string, sequence uint64) string { return fmt.Sprintf("%s/", KeyPacketCommitmentPrefix) + channelPath(portID, channelID) + fmt.Sprintf("/packets/%d", sequence) } +// PacketCommitmentPrefixPath defines the prefix for commitments to packet data fields store path. +func PacketCommitmentPrefixPath(portID, channelID string) string { + return fmt.Sprintf("%s/", KeyPacketCommitmentPrefix) + channelPath(portID, channelID) +} + // PacketAcknowledgementPath defines the packet acknowledgement store path func PacketAcknowledgementPath(portID, channelID string, sequence uint64) string { return fmt.Sprintf("%s/", KeyPacketAckPrefix) + channelPath(portID, channelID) + fmt.Sprintf("/acknowledgements/%d", sequence) diff --git a/x/ibc/keeper/querier.go b/x/ibc/keeper/querier.go index 965085e151..40104a8feb 100644 --- a/x/ibc/keeper/querier.go +++ b/x/ibc/keeper/querier.go @@ -43,11 +43,17 @@ func NewQuerier(k Keeper) sdk.Querier { res, err = channel.QuerierChannels(ctx, req, k.ChannelKeeper) case channel.QueryConnectionChannels: res, err = channel.QuerierConnectionChannels(ctx, req, k.ChannelKeeper) + case channel.QueryPacketCommitments: + res, err = channel.QuerierPacketCommitments(ctx, req, k.ChannelKeeper) + case channel.QueryUnrelayedAcknowledgements: + res, err = channel.QuerierUnrelayedAcknowledgements(ctx, req, k.ChannelKeeper) + case channel.QueryUnrelayedPacketSends: + res, err = channel.QuerierUnrelayedPacketSends(ctx, req, k.ChannelKeeper) default: err = sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown IBC %s query endpoint", channel.SubModuleName) } default: - err = sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown IBC query endpoint") + err = sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown IBC query endpoint") } return res, err diff --git a/x/ibc/keeper/querier_test.go b/x/ibc/keeper/querier_test.go index c2bc7abc41..54bd4bf126 100644 --- a/x/ibc/keeper/querier_test.go +++ b/x/ibc/keeper/querier_test.go @@ -91,6 +91,24 @@ func (suite *KeeperTestSuite) TestNewQuerier() { false, "", }, + { + "channel - QuerierPacketCommitments", + []string{channel.SubModuleName, channel.QueryPacketCommitments}, + false, + "", + }, + { + "channel - QuerierUnrelayedAcknowledgements", + []string{channel.SubModuleName, channel.QueryUnrelayedAcknowledgements}, + false, + "", + }, + { + "channel - QuerierUnrelayedPacketSends", + []string{channel.SubModuleName, channel.QueryUnrelayedPacketSends}, + false, + "", + }, { "channel - invalid query", []string{channel.SubModuleName, "foo"},