From ca20a399621c2f14aba1473f78f09b464dc8065d Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Mon, 18 May 2020 12:50:07 -0400 Subject: [PATCH] x/ibc: constrain acks processing order (#6244) * x/ibc: constrain acks processing order * test * address @AdityaSripal comments * address @colin-axner comments * address @alexanderbez comments --- x/ibc-transfer/keeper/relay.go | 6 ++- x/ibc/04-channel/genesis.go | 4 ++ x/ibc/04-channel/keeper/handshake.go | 2 + x/ibc/04-channel/keeper/keeper.go | 67 ++++++++++++++++++-------- x/ibc/04-channel/keeper/keeper_test.go | 14 +++++- x/ibc/04-channel/keeper/packet.go | 29 ++++++++++- x/ibc/04-channel/keeper/packet_test.go | 27 ++++++++++- x/ibc/04-channel/types/errors.go | 9 ++-- x/ibc/04-channel/types/genesis.go | 11 ++++- x/ibc/04-channel/types/genesis_test.go | 12 +++++ x/ibc/24-host/keys.go | 26 +++++----- x/ibc/24-host/utils.go | 30 ++++++++++++ x/ibc/genesis_test.go | 3 ++ 13 files changed, 195 insertions(+), 45 deletions(-) diff --git a/x/ibc-transfer/keeper/relay.go b/x/ibc-transfer/keeper/relay.go index 25e9e2b081..ec711067b0 100644 --- a/x/ibc-transfer/keeper/relay.go +++ b/x/ibc-transfer/keeper/relay.go @@ -40,7 +40,11 @@ func (k Keeper) SendTransfer( // get the next sequence sequence, found := k.channelKeeper.GetNextSequenceSend(ctx, sourcePort, sourceChannel) if !found { - return channeltypes.ErrSequenceSendNotFound + return sdkerrors.Wrapf( + channeltypes.ErrSequenceSendNotFound, + "source port: %s, source channel: %s", sourcePort, sourceChannel, + ) + } return k.createOutgoingPacket(ctx, sequence, sourcePort, sourceChannel, destinationPort, destinationChannel, destHeight, amount, sender, receiver) diff --git a/x/ibc/04-channel/genesis.go b/x/ibc/04-channel/genesis.go index c44767a405..f1cea375aa 100644 --- a/x/ibc/04-channel/genesis.go +++ b/x/ibc/04-channel/genesis.go @@ -23,6 +23,9 @@ func InitGenesis(ctx sdk.Context, k Keeper, gs GenesisState) { for _, rs := range gs.RecvSequences { k.SetNextSequenceRecv(ctx, rs.PortID, rs.ChannelID, rs.Sequence) } + for _, as := range gs.AckSequences { + k.SetNextSequenceAck(ctx, as.PortID, as.ChannelID, as.Sequence) + } } // ExportGenesis returns the ibc channel submodule's exported genesis. @@ -33,5 +36,6 @@ func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState { Commitments: k.GetAllPacketCommitments(ctx), SendSequences: k.GetAllPacketSendSeqs(ctx), RecvSequences: k.GetAllPacketRecvSeqs(ctx), + AckSequences: k.GetAllPacketAckSeqs(ctx), } } diff --git a/x/ibc/04-channel/keeper/handshake.go b/x/ibc/04-channel/keeper/handshake.go index 2196a956ad..a141ce315e 100644 --- a/x/ibc/04-channel/keeper/handshake.go +++ b/x/ibc/04-channel/keeper/handshake.go @@ -74,6 +74,7 @@ func (k Keeper) ChanOpenInit( k.SetNextSequenceSend(ctx, portID, channelID, 1) k.SetNextSequenceRecv(ctx, portID, channelID, 1) + k.SetNextSequenceAck(ctx, portID, channelID, 1) k.Logger(ctx).Info(fmt.Sprintf("channel (port-id: %s, channel-id: %s) state updated: NONE -> INIT", portID, channelID)) return capKey, nil @@ -155,6 +156,7 @@ func (k Keeper) ChanOpenTry( k.SetNextSequenceSend(ctx, portID, channelID, 1) k.SetNextSequenceRecv(ctx, portID, channelID, 1) + k.SetNextSequenceAck(ctx, portID, channelID, 1) k.Logger(ctx).Info(fmt.Sprintf("channel (port-id: %s, channel-id: %s) state updated: NONE -> TRYOPEN", portID, channelID)) return capKey, nil diff --git a/x/ibc/04-channel/keeper/keeper.go b/x/ibc/04-channel/keeper/keeper.go index b08cc0d09e..60664a1f31 100644 --- a/x/ibc/04-channel/keeper/keeper.go +++ b/x/ibc/04-channel/keeper/keeper.go @@ -1,7 +1,6 @@ package keeper import ( - "encoding/binary" "fmt" "strconv" "strings" @@ -76,7 +75,7 @@ func (k Keeper) GetNextSequenceSend(ctx sdk.Context, portID, channelID string) ( return 0, false } - return binary.BigEndian.Uint64(bz), true + return sdk.BigEndianToUint64(bz), true } // SetNextSequenceSend sets a channel's next send sequence to the store @@ -94,7 +93,7 @@ func (k Keeper) GetNextSequenceRecv(ctx sdk.Context, portID, channelID string) ( return 0, false } - return binary.BigEndian.Uint64(bz), true + return sdk.BigEndianToUint64(bz), true } // SetNextSequenceRecv sets a channel's next receive sequence to the store @@ -104,6 +103,24 @@ func (k Keeper) SetNextSequenceRecv(ctx sdk.Context, portID, channelID string, s store.Set(host.KeyNextSequenceRecv(portID, channelID), bz) } +// GetNextSequenceAck gets a channel's next ack sequence from the store +func (k Keeper) GetNextSequenceAck(ctx sdk.Context, portID, channelID string) (uint64, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(host.KeyNextSequenceAck(portID, channelID)) + if bz == nil { + return 0, false + } + + return sdk.BigEndianToUint64(bz), true +} + +// SetNextSequenceAck sets a channel's next ack sequence to the store +func (k Keeper) SetNextSequenceAck(ctx sdk.Context, portID, channelID string, sequence uint64) { + store := ctx.KVStore(k.storeKey) + bz := sdk.Uint64ToBigEndian(sequence) + store.Set(host.KeyNextSequenceAck(portID, channelID), bz) +} + // GetPacketCommitment gets the packet commitment hash from the store func (k Keeper) GetPacketCommitment(ctx sdk.Context, portID, channelID string, sequence uint64) []byte { store := ctx.KVStore(k.storeKey) @@ -138,23 +155,17 @@ func (k Keeper) GetPacketAcknowledgement(ctx sdk.Context, portID, channelID stri return bz, true } -// IteratePacketSequence provides an iterator over all send and receive sequences. For each -// sequence, cb will be called. If the cb returns true, the iterator will close -// and stop. -func (k Keeper) IteratePacketSequence(ctx sdk.Context, send bool, cb func(portID, channelID string, sequence uint64) bool) { - store := ctx.KVStore(k.storeKey) - var iterator db.Iterator - if send { - iterator = sdk.KVStorePrefixIterator(store, []byte(host.KeyNextSeqSendPrefix)) - } else { - iterator = sdk.KVStorePrefixIterator(store, []byte(host.KeyNextSeqRecvPrefix)) - } - +// IteratePacketSequence provides an iterator over all send, receive or ack sequences. +// For each sequence, cb will be called. If the cb returns true, the iterator +// will close and stop. +func (k Keeper) IteratePacketSequence(ctx sdk.Context, iterator db.Iterator, cb func(portID, channelID string, sequence uint64) bool) { defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - keySplit := strings.Split(string(iterator.Key()), "/") - portID := keySplit[2] - channelID := keySplit[4] + portID, channelID, err := host.ParseChannelPath(string(iterator.Key())) + if err != nil { + // return if the key is not a channel key + return + } sequence := sdk.BigEndianToUint64(iterator.Value()) @@ -166,7 +177,9 @@ func (k Keeper) IteratePacketSequence(ctx sdk.Context, send bool, cb func(portID // GetAllPacketSendSeqs returns all stored next send sequences. func (k Keeper) GetAllPacketSendSeqs(ctx sdk.Context) (seqs []types.PacketSequence) { - k.IteratePacketSequence(ctx, true, func(portID, channelID string, nextSendSeq uint64) bool { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte(host.KeyNextSeqSendPrefix)) + k.IteratePacketSequence(ctx, iterator, func(portID, channelID string, nextSendSeq uint64) bool { ps := types.NewPacketSequence(portID, channelID, nextSendSeq) seqs = append(seqs, ps) return false @@ -176,7 +189,9 @@ func (k Keeper) GetAllPacketSendSeqs(ctx sdk.Context) (seqs []types.PacketSequen // GetAllPacketRecvSeqs returns all stored next recv sequences. func (k Keeper) GetAllPacketRecvSeqs(ctx sdk.Context) (seqs []types.PacketSequence) { - k.IteratePacketSequence(ctx, false, func(portID, channelID string, nextRecvSeq uint64) bool { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte(host.KeyNextSeqRecvPrefix)) + k.IteratePacketSequence(ctx, iterator, func(portID, channelID string, nextRecvSeq uint64) bool { ps := types.NewPacketSequence(portID, channelID, nextRecvSeq) seqs = append(seqs, ps) return false @@ -184,6 +199,18 @@ func (k Keeper) GetAllPacketRecvSeqs(ctx sdk.Context) (seqs []types.PacketSequen return seqs } +// GetAllPacketAckSeqs returns all stored next acknowledgements sequences. +func (k Keeper) GetAllPacketAckSeqs(ctx sdk.Context) (seqs []types.PacketSequence) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte(host.KeyNextSeqAckPrefix)) + k.IteratePacketSequence(ctx, iterator, func(portID, channelID string, nextAckSeq uint64) bool { + ps := types.NewPacketSequence(portID, channelID, nextAckSeq) + seqs = append(seqs, ps) + return false + }) + return seqs +} + // IteratePacketCommitment provides an iterator over all PacketCommitment objects. For each // aknowledgement, cb will be called. If the cb returns true, the iterator will close // and stop. diff --git a/x/ibc/04-channel/keeper/keeper_test.go b/x/ibc/04-channel/keeper/keeper_test.go index 6c13b67c90..847131d6bf 100644 --- a/x/ibc/04-channel/keeper/keeper_test.go +++ b/x/ibc/04-channel/keeper/keeper_test.go @@ -131,15 +131,19 @@ func (suite KeeperTestSuite) TestGetAllSequences() { for _, seq := range expSeqs { suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(ctx, seq.PortID, seq.ChannelID, seq.Sequence) suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(ctx, seq.PortID, seq.ChannelID, seq.Sequence) + suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceAck(ctx, seq.PortID, seq.ChannelID, seq.Sequence) } sendSeqs := suite.chainB.App.IBCKeeper.ChannelKeeper.GetAllPacketSendSeqs(ctx) recvSeqs := suite.chainB.App.IBCKeeper.ChannelKeeper.GetAllPacketRecvSeqs(ctx) + ackSeqs := suite.chainB.App.IBCKeeper.ChannelKeeper.GetAllPacketAckSeqs(ctx) suite.Require().Len(sendSeqs, 2) suite.Require().Len(recvSeqs, 2) + suite.Require().Len(ackSeqs, 2) suite.Require().Equal(expSeqs, sendSeqs) suite.Require().Equal(expSeqs, recvSeqs) + suite.Require().Equal(expSeqs, ackSeqs) } func (suite KeeperTestSuite) TestGetAllCommitmentsAcks() { @@ -175,9 +179,13 @@ func (suite *KeeperTestSuite) TestSetSequence() { _, found = suite.chainB.App.IBCKeeper.ChannelKeeper.GetNextSequenceRecv(ctx, testPort1, testChannel1) suite.False(found) - nextSeqSend, nextSeqRecv := uint64(10), uint64(10) + _, found = suite.chainB.App.IBCKeeper.ChannelKeeper.GetNextSequenceAck(ctx, testPort1, testChannel1) + suite.False(found) + + nextSeqSend, nextSeqRecv, nextSeqAck := uint64(10), uint64(10), uint64(10) suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(ctx, testPort1, testChannel1, nextSeqSend) suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(ctx, testPort1, testChannel1, nextSeqRecv) + suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceAck(ctx, testPort1, testChannel1, nextSeqAck) storedNextSeqSend, found := suite.chainB.App.IBCKeeper.ChannelKeeper.GetNextSequenceSend(ctx, testPort1, testChannel1) suite.True(found) @@ -186,6 +194,10 @@ func (suite *KeeperTestSuite) TestSetSequence() { storedNextSeqRecv, found := suite.chainB.App.IBCKeeper.ChannelKeeper.GetNextSequenceSend(ctx, testPort1, testChannel1) suite.True(found) suite.Equal(nextSeqRecv, storedNextSeqRecv) + + storedNextSeqAck, found := suite.chainB.App.IBCKeeper.ChannelKeeper.GetNextSequenceAck(ctx, testPort1, testChannel1) + suite.True(found) + suite.Equal(nextSeqAck, storedNextSeqAck) } func (suite *KeeperTestSuite) TestPackageCommitment() { diff --git a/x/ibc/04-channel/keeper/packet.go b/x/ibc/04-channel/keeper/packet.go index fa514ffc36..bb90d69af6 100644 --- a/x/ibc/04-channel/keeper/packet.go +++ b/x/ibc/04-channel/keeper/packet.go @@ -99,7 +99,10 @@ func (k Keeper) SendPacket( nextSequenceSend, found := k.GetNextSequenceSend(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) if !found { - return types.ErrSequenceSendNotFound + return sdkerrors.Wrapf( + types.ErrSequenceSendNotFound, + "source port: %s, source channel: %s", packet.GetSourcePort(), packet.GetSourceChannel(), + ) } if packet.GetSequence() != nextSequenceSend { @@ -247,7 +250,10 @@ func (k Keeper) PacketExecuted( if channel.Ordering == types.ORDERED { nextSequenceRecv, found := k.GetNextSequenceRecv(ctx, packet.GetDestPort(), packet.GetDestChannel()) if !found { - return types.ErrSequenceReceiveNotFound + return sdkerrors.Wrapf( + types.ErrSequenceReceiveNotFound, + "destination port: %s, destination channel: %s", packet.GetDestPort(), packet.GetDestChannel(), + ) } if packet.GetSequence() != nextSequenceRecv { @@ -352,6 +358,25 @@ func (k Keeper) AcknowledgePacket( return nil, sdkerrors.Wrap(err, "invalid acknowledgement on counterparty chain") } + if channel.Ordering == types.ORDERED { + nextSequenceAck, found := k.GetNextSequenceAck(ctx, packet.GetDestPort(), packet.GetDestChannel()) + if !found { + return nil, sdkerrors.Wrapf( + types.ErrSequenceAckNotFound, + "destination port: %s, destination channel: %s", packet.GetDestPort(), packet.GetDestChannel(), + ) + } + + if packet.GetSequence() != nextSequenceAck { + return nil, sdkerrors.Wrapf( + sdkerrors.ErrInvalidSequence, + "packet sequence ≠ next ack sequence (%d ≠ %d)", packet.GetSequence(), nextSequenceAck, + ) + } + + k.SetNextSequenceAck(ctx, packet.GetDestPort(), packet.GetDestChannel(), nextSequenceAck+1) + } + // log that a packet has been acknowledged k.Logger(ctx).Info(fmt.Sprintf("packet acknowledged: %v", packet)) diff --git a/x/ibc/04-channel/keeper/packet_test.go b/x/ibc/04-channel/keeper/packet_test.go index d0d5aa62eb..379430ec14 100644 --- a/x/ibc/04-channel/keeper/packet_test.go +++ b/x/ibc/04-channel/keeper/packet_test.go @@ -275,6 +275,7 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.ORDERED, testConnectionIDB) suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 1, types.CommitPacket(packet)) suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), testPort2, testChannel2, 1, types.CommitAcknowledgement(ack)) + suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceAck(suite.chainB.GetContext(), counterparty.GetPortID(), counterparty.GetChannelID(), 1) }, true}, {"channel not found", func() {}, false}, {"channel not open", func() { @@ -309,6 +310,29 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 1, types.CommitPacket(packet)) }, false}, + {"next ack sequence not found", func() { + packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) + suite.chainB.CreateClient(suite.chainA) + suite.chainA.CreateClient(suite.chainB) + suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) + suite.chainA.createConnection(testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, connection.OPEN) + suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.ORDERED, testConnectionIDB) + suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 1, types.CommitPacket(packet)) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), testPort2, testChannel2, 1, types.CommitAcknowledgement(ack)) + }, false}, + {"next ack sequence mismatch", func() { + packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) + suite.chainB.CreateClient(suite.chainA) + suite.chainA.CreateClient(suite.chainB) + suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) + suite.chainA.createConnection(testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, connection.OPEN) + suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.ORDERED, testConnectionIDB) + suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 1, types.CommitPacket(packet)) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), testPort2, testChannel2, 1, types.CommitAcknowledgement(ack)) + suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceAck(suite.chainB.GetContext(), counterparty.GetPortID(), counterparty.GetChannelID(), 10) + }, false}, } for i, tc := range testCases { @@ -322,12 +346,11 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { proof, proofHeight := queryProof(suite.chainA, packetKey) ctx := suite.chainB.GetContext() + packetOut, err := suite.chainB.App.IBCKeeper.ChannelKeeper.AcknowledgePacket(ctx, packet, ack, proof, proofHeight+1) if tc.expPass { - packetOut, err := suite.chainB.App.IBCKeeper.ChannelKeeper.AcknowledgePacket(ctx, packet, ack, proof, proofHeight+1) suite.Require().NoError(err) suite.Require().NotNil(packetOut) } else { - packetOut, err := suite.chainB.App.IBCKeeper.ChannelKeeper.AcknowledgePacket(ctx, packet, ack, proof, proofHeight+1) suite.Require().Error(err) suite.Require().Nil(packetOut) } diff --git a/x/ibc/04-channel/types/errors.go b/x/ibc/04-channel/types/errors.go index 6147975a31..692b0feade 100644 --- a/x/ibc/04-channel/types/errors.go +++ b/x/ibc/04-channel/types/errors.go @@ -16,8 +16,9 @@ var ( ErrChannelCapabilityNotFound = sdkerrors.Register(SubModuleName, 9, "channel capability not found") ErrSequenceSendNotFound = sdkerrors.Register(SubModuleName, 10, "sequence send not found") ErrSequenceReceiveNotFound = sdkerrors.Register(SubModuleName, 11, "sequence receive not found") - ErrInvalidPacket = sdkerrors.Register(SubModuleName, 12, "invalid packet") - ErrPacketTimeout = sdkerrors.Register(SubModuleName, 13, "packet timeout") - ErrTooManyConnectionHops = sdkerrors.Register(SubModuleName, 14, "too many connection hops") - ErrAcknowledgementTooLong = sdkerrors.Register(SubModuleName, 15, "acknowledgement too long") + ErrSequenceAckNotFound = sdkerrors.Register(SubModuleName, 12, "sequence acknowledgement not found") + ErrInvalidPacket = sdkerrors.Register(SubModuleName, 13, "invalid packet") + ErrPacketTimeout = sdkerrors.Register(SubModuleName, 14, "packet timeout") + ErrTooManyConnectionHops = sdkerrors.Register(SubModuleName, 15, "too many connection hops") + ErrAcknowledgementTooLong = sdkerrors.Register(SubModuleName, 16, "acknowledgement too long") ) diff --git a/x/ibc/04-channel/types/genesis.go b/x/ibc/04-channel/types/genesis.go index 15fb092b8a..cc1a3a519c 100644 --- a/x/ibc/04-channel/types/genesis.go +++ b/x/ibc/04-channel/types/genesis.go @@ -65,12 +65,13 @@ type GenesisState struct { Commitments []PacketAckCommitment `json:"commitments" yaml:"commitments"` SendSequences []PacketSequence `json:"send_sequences" yaml:"send_sequences"` RecvSequences []PacketSequence `json:"recv_sequences" yaml:"recv_sequences"` + AckSequences []PacketSequence `json:"ack_sequences" yaml:"ack_sequences"` } // NewGenesisState creates a GenesisState instance. func NewGenesisState( channels []IdentifiedChannel, acks, commitments []PacketAckCommitment, - sendSeqs, recvSeqs []PacketSequence, + sendSeqs, recvSeqs, ackSeqs []PacketSequence, ) GenesisState { return GenesisState{ Channels: channels, @@ -78,6 +79,7 @@ func NewGenesisState( Commitments: commitments, SendSequences: sendSeqs, RecvSequences: recvSeqs, + AckSequences: ackSeqs, } } @@ -89,6 +91,7 @@ func DefaultGenesisState() GenesisState { Commitments: []PacketAckCommitment{}, SendSequences: []PacketSequence{}, RecvSequences: []PacketSequence{}, + AckSequences: []PacketSequence{}, } } @@ -125,6 +128,12 @@ func (gs GenesisState) Validate() error { } } + for i, as := range gs.AckSequences { + if err := as.Validate(); err != nil { + return fmt.Errorf("invalid acknowledgement sequence %d: %w", i, err) + } + } + return nil } diff --git a/x/ibc/04-channel/types/genesis_test.go b/x/ibc/04-channel/types/genesis_test.go index f0ba97eb84..0228066f50 100644 --- a/x/ibc/04-channel/types/genesis_test.go +++ b/x/ibc/04-channel/types/genesis_test.go @@ -58,6 +58,9 @@ func TestValidateGenesis(t *testing.T) { []PacketSequence{ NewPacketSequence(testPort2, testChannel2, 1), }, + []PacketSequence{ + NewPacketSequence(testPort2, testChannel2, 1), + }, ), expPass: true, }, @@ -119,6 +122,15 @@ func TestValidateGenesis(t *testing.T) { }, expPass: false, }, + { + name: "invalid ack seq", + genState: GenesisState{ + AckSequences: []PacketSequence{ + NewPacketSequence(testPort1, "(testChannel1)", 1), + }, + }, + expPass: false, + }, } for _, tc := range testCases { diff --git a/x/ibc/24-host/keys.go b/x/ibc/24-host/keys.go index 23cb060edb..7d2757f47f 100644 --- a/x/ibc/24-host/keys.go +++ b/x/ibc/24-host/keys.go @@ -2,7 +2,6 @@ package host import ( "fmt" - "strings" ) const ( @@ -31,6 +30,7 @@ const ( KeyChannelCapabilityPrefix = "capabilities" KeyNextSeqSendPrefix = "seqSends" KeyNextSeqRecvPrefix = "seqRecvs" + KeyNextSeqAckPrefix = "seqAcks" KeyPacketCommitmentPrefix = "commitments" KeyPacketAckPrefix = "acks" ) @@ -129,6 +129,11 @@ func NextSequenceRecvPath(portID, channelID string) string { return fmt.Sprintf("%s/", KeyNextSeqRecvPrefix) + channelPath(portID, channelID) + "/nextSequenceRecv" } +// NextSequenceAckPath defines the next acknowledgement sequence counter store path +func NextSequenceAckPath(portID, channelID string) string { + return fmt.Sprintf("%s/", KeyNextSeqAckPrefix) + channelPath(portID, channelID) + "/nextSequenceAck" +} + // PacketCommitmentPath defines the commitments to packet data fields store path func PacketCommitmentPath(portID, channelID string, sequence uint64) string { return fmt.Sprintf("%s/", KeyPacketCommitmentPrefix) + channelPath(portID, channelID) + fmt.Sprintf("/packets/%d", sequence) @@ -156,6 +161,12 @@ func KeyNextSequenceRecv(portID, channelID string) []byte { return []byte(NextSequenceRecvPath(portID, channelID)) } +// KeyNextSequenceAck returns the store key for the acknowledgement sequence of +// a particular channel binded to a specific port. +func KeyNextSequenceAck(portID, channelID string) []byte { + return []byte(NextSequenceAckPath(portID, channelID)) +} + // KeyPacketCommitment returns the store key of under which a packet commitment // is stored func KeyPacketCommitment(portID, channelID string, sequence uint64) []byte { @@ -172,19 +183,6 @@ func channelPath(portID, channelID string) string { return fmt.Sprintf("ports/%s/channels/%s", portID, channelID) } -func MustParseChannelPath(path string) (string, string) { - split := strings.Split(path, "/") - if len(split) != 5 { - panic("cannot parse channel path") - } - - if split[1] != "ports" || split[3] != "channels" { - panic("cannot parse channel path") - } - - return split[2], split[4] -} - // ICS05 // The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-005-port-allocation#store-paths diff --git a/x/ibc/24-host/utils.go b/x/ibc/24-host/utils.go index c75f356561..2e9c91b183 100644 --- a/x/ibc/24-host/utils.go +++ b/x/ibc/24-host/utils.go @@ -1,5 +1,10 @@ package host +import ( + "fmt" + "strings" +) + // RemovePath is an util function to remove a path from a set. func RemovePath(paths []string, path string) ([]string, bool) { for i, p := range paths { @@ -9,3 +14,28 @@ func RemovePath(paths []string, path string) ([]string, bool) { } return paths, false } + +// ParseChannelPath returns the port and channel ID from a full path. It returns +// an error if the provided path is invalid, +func ParseChannelPath(path string) (string, string, error) { + split := strings.Split(path, "/") + if len(split) < 5 { + return "", "", fmt.Errorf("cannot parse channel path %s", path) + } + + if split[1] != "ports" || split[3] != "channels" { + return "", "", fmt.Errorf("cannot parse channel path %s", path) + } + + return split[2], split[4], nil +} + +// MustParseChannelPath returns the port and channel ID from a full path. Panics +// if the provided path is invalid +func MustParseChannelPath(path string) (string, string) { + portID, channelID, err := ParseChannelPath(path) + if err != nil { + panic(err) + } + return portID, channelID +} diff --git a/x/ibc/genesis_test.go b/x/ibc/genesis_test.go index 8cd9bf289f..cd64b18dd7 100644 --- a/x/ibc/genesis_test.go +++ b/x/ibc/genesis_test.go @@ -74,6 +74,9 @@ func (suite *IBCTestSuite) TestValidateGenesis() { []channel.PacketSequence{ channel.NewPacketSequence(port2, channel2, 1), }, + []channel.PacketSequence{ + channel.NewPacketSequence(port2, channel2, 1), + }, ), }, expPass: true,