x/ibc: constrain acks processing order (#6244)

* x/ibc: constrain acks processing order

* test

* address @AdityaSripal comments

* address @colin-axner comments

* address @alexanderbez comments
This commit is contained in:
Federico Kunze 2020-05-18 12:50:07 -04:00 committed by GitHub
parent 0db51de37a
commit ca20a39962
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 195 additions and 45 deletions

View File

@ -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)

View File

@ -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),
}
}

View File

@ -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

View File

@ -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.

View File

@ -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() {

View File

@ -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))

View File

@ -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)
}

View File

@ -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")
)

View File

@ -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
}

View File

@ -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 {

View File

@ -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

View File

@ -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
}

View File

@ -74,6 +74,9 @@ func (suite *IBCTestSuite) TestValidateGenesis() {
[]channel.PacketSequence{
channel.NewPacketSequence(port2, channel2, 1),
},
[]channel.PacketSequence{
channel.NewPacketSequence(port2, channel2, 1),
},
),
},
expPass: true,