From 995c1f1eff813f667cdf6f4f5849c393ad0dead4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Colin=20Axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Wed, 12 Apr 2023 10:17:12 +0200 Subject: [PATCH 01/18] added validateProposedUpgradeFields function and CheckIsOpen helper function --- modules/core/03-connection/keeper/keeper.go | 12 + modules/core/04-channel/keeper/upgrade.go | 24 + modules/core/04-channel/types/errors.go | 1 + .../core/04-channel/types/expected_keepers.go | 2 + modules/core/04-channel/types/upgrade.go | 43 ++ modules/core/04-channel/types/upgrade.pb.go | 582 +++++++++++++++++- proto/ibc/core/channel/v1/upgrade.proto | 19 + 7 files changed, 660 insertions(+), 23 deletions(-) create mode 100644 modules/core/04-channel/types/upgrade.go diff --git a/modules/core/03-connection/keeper/keeper.go b/modules/core/03-connection/keeper/keeper.go index 4e763c3302b..7af8b8e110f 100644 --- a/modules/core/03-connection/keeper/keeper.go +++ b/modules/core/03-connection/keeper/keeper.go @@ -83,6 +83,18 @@ func (k Keeper) HasConnection(ctx sdk.Context, connectionID string) bool { return store.Has(host.ConnectionKey(connectionID)) } +// CheckIsOpen returns nil if the connection with the given id is OPEN. Otherwise an error is returned. +func (k Keeper) CheckIsOpen(ctx sdk.Context, connectionID string) error { + connection, found := k.GetConnection(ctx, connectionID) + if !found { + return errorsmod.Wrapf(types.ErrConnectionNotFound, "failed to retrieve connection: %s", connectionID) + } + if connection.State != types.OPEN { + return errorsmod.Wrapf(types.ErrInvalidConnectionState, "connectionID (%s) state is not OPEN (got %s)", connectionID, connection.State.String()) + } + return nil +} + // SetConnection sets a connection to the store func (k Keeper) SetConnection(ctx sdk.Context, connectionID string, connection types.ConnectionEnd) { store := ctx.KVStore(k.storeKey) diff --git a/modules/core/04-channel/keeper/upgrade.go b/modules/core/04-channel/keeper/upgrade.go index 90537cc7a4c..c08e059321b 100644 --- a/modules/core/04-channel/keeper/upgrade.go +++ b/modules/core/04-channel/keeper/upgrade.go @@ -134,3 +134,27 @@ func (k Keeper) RestoreChannel(ctx sdk.Context, portID, channelID string, upgrad return cbs.OnChanUpgradeRestore(ctx, portID, channelID) } + +// validateProposedUpgradeFields validates the proposed upgrade fields against the existing channel. +// It returns an error if the following constraints are not met: +// - there exists at least one valid proposed change to the existing channel fields +// - the proposed order is a subset of the existing order +// - the proposed connection hops do not exist +// - the proposed version is non-empty (checked in ModifiableUpgradeFields.ValidateBasic()) +// - the proposed connection hops are not open +func (k Keeper) validateProposedUpgradeFields(ctx sdk.Context, proposedUpgrade types.ModifiableUpgradeFields, existingChannel types.Channel) error { + currentFields := types.ModifiableUpgradeFields{ + Ordering: existingChannel.Ordering, + ConnectionHops: existingChannel.ConnectionHops, + Version: existingChannel.Version, + } + if reflect.DeepEqual(proposedUpgrade, currentFields) { + return errorsmod.Wrap(types.ErrChannelExists, "existing channel end is identical to proposed upgrade channel end") + } + + if !currentFields.Ordering.SubsetOf(proposedUpgrade.Ordering) { + return errorsmod.Wrap(types.ErrInvalidChannelOrdering, "channel ordering must be a subset of the new ordering") + } + + return k.connectionKeeper.CheckIsOpen(ctx, proposedUpgrade.ConnectionHops[0]) +} diff --git a/modules/core/04-channel/types/errors.go b/modules/core/04-channel/types/errors.go index 876d0e8dd21..19c10c66598 100644 --- a/modules/core/04-channel/types/errors.go +++ b/modules/core/04-channel/types/errors.go @@ -43,4 +43,5 @@ var ( ErrInvalidUpgradeTimeout = errorsmod.Register(SubModuleName, 27, "either timeout height or timeout timestamp must be non-zero") ErrUpgradeSequenceNotFound = errorsmod.Register(SubModuleName, 28, "upgrade sequence not found") ErrUpgradeErrorNotFound = errorsmod.Register(SubModuleName, 29, "upgrade error receipt not found") + ErrInvalidUpgrade = errorsmod.Register(SubModuleName, 30, "invalid upgrade") ) diff --git a/modules/core/04-channel/types/expected_keepers.go b/modules/core/04-channel/types/expected_keepers.go index 3c0b0b0198d..b7552470134 100644 --- a/modules/core/04-channel/types/expected_keepers.go +++ b/modules/core/04-channel/types/expected_keepers.go @@ -18,6 +18,8 @@ type ClientKeeper interface { // ConnectionKeeper expected account IBC connection keeper type ConnectionKeeper interface { + CheckIsOpen(ctx sdk.Context, connectionID string) error + HasConnection(ctx sdk.Context, connectionID string) bool GetConnection(ctx sdk.Context, connectionID string) (connectiontypes.ConnectionEnd, bool) GetTimestampAtHeight( ctx sdk.Context, diff --git a/modules/core/04-channel/types/upgrade.go b/modules/core/04-channel/types/upgrade.go new file mode 100644 index 00000000000..8daed670bfe --- /dev/null +++ b/modules/core/04-channel/types/upgrade.go @@ -0,0 +1,43 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + + "github.com/cosmos/ibc-go/v7/internal/collections" +) + +// ValidateBasic performs a basic validation of the upgrade fields +func (u Upgrade) ValidateBasic() error { + if err := u.UpgradeFields.ValidateBasic(); err != nil { + return errorsmod.Wrap(err, "proposed upgrade fields are invalid") + } + + if !u.Timeout.IsValid() { + return errorsmod.Wrap(ErrInvalidUpgrade, "upgrade timeout cannot be empty") + } + + // TODO: determine if last packet sequence sent can be 0? + return nil +} + +// ValidateBasic performs a basic validation of the proposed upgrade fields +func (muf ModifiableUpgradeFields) ValidateBasic() error { + if !collections.Contains(muf.Ordering, []Order{ORDERED, UNORDERED}) { + return errorsmod.Wrap(ErrInvalidChannelOrdering, muf.Ordering.String()) + } + + if len(muf.ConnectionHops) != 1 { + return errorsmod.Wrap(ErrTooManyConnectionHops, "current IBC version only supports one connection hop") + } + + if muf.Version == "" { + return errorsmod.Wrap(ErrInvalidUpgrade, "proposed upgrade version cannot be empty") + } + + return nil +} + +// IsValid returns true if either the height or timestamp is non-zero +func (ut UpgradeTimeout) IsValid() bool { + return !ut.TimeoutHeight.IsZero() || ut.TimeoutTimestamp != 0 +} diff --git a/modules/core/04-channel/types/upgrade.pb.go b/modules/core/04-channel/types/upgrade.pb.go index 21c26428c39..df233e756ef 100644 --- a/modules/core/04-channel/types/upgrade.pb.go +++ b/modules/core/04-channel/types/upgrade.pb.go @@ -24,6 +24,132 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// Upgrade is a verifiable type which contains the relevant information +// for an attempted upgrade. It provides the proposed changes to the channel +// end, the timeout for this upgrade attempt and the last packet sequence sent +// to allow the counterparty to block sends after the upgrade has started. +type Upgrade struct { + UpgradeFields ModifiableUpgradeFields `protobuf:"bytes,1,opt,name=upgrade_fields,json=upgradeFields,proto3" json:"upgrade_fields"` + Timeout UpgradeTimeout `protobuf:"bytes,2,opt,name=timeout,proto3" json:"timeout"` + LastPacketSent uint64 `protobuf:"varint,3,opt,name=last_packet_sent,json=lastPacketSent,proto3" json:"last_packet_sent,omitempty"` +} + +func (m *Upgrade) Reset() { *m = Upgrade{} } +func (m *Upgrade) String() string { return proto.CompactTextString(m) } +func (*Upgrade) ProtoMessage() {} +func (*Upgrade) Descriptor() ([]byte, []int) { + return fileDescriptor_fb1cef68588848b2, []int{0} +} +func (m *Upgrade) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Upgrade) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Upgrade.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Upgrade) XXX_Merge(src proto.Message) { + xxx_messageInfo_Upgrade.Merge(m, src) +} +func (m *Upgrade) XXX_Size() int { + return m.Size() +} +func (m *Upgrade) XXX_DiscardUnknown() { + xxx_messageInfo_Upgrade.DiscardUnknown(m) +} + +var xxx_messageInfo_Upgrade proto.InternalMessageInfo + +func (m *Upgrade) GetUpgradeFields() ModifiableUpgradeFields { + if m != nil { + return m.UpgradeFields + } + return ModifiableUpgradeFields{} +} + +func (m *Upgrade) GetTimeout() UpgradeTimeout { + if m != nil { + return m.Timeout + } + return UpgradeTimeout{} +} + +func (m *Upgrade) GetLastPacketSent() uint64 { + if m != nil { + return m.LastPacketSent + } + return 0 +} + +// ModifiableUpgradeFields are the fields in a channel end which may be changed +// during a channel upgrade. +type ModifiableUpgradeFields struct { + Ordering Order `protobuf:"varint,1,opt,name=ordering,proto3,enum=ibc.core.channel.v1.Order" json:"ordering,omitempty"` + ConnectionHops []string `protobuf:"bytes,2,rep,name=connection_hops,json=connectionHops,proto3" json:"connection_hops,omitempty"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` +} + +func (m *ModifiableUpgradeFields) Reset() { *m = ModifiableUpgradeFields{} } +func (m *ModifiableUpgradeFields) String() string { return proto.CompactTextString(m) } +func (*ModifiableUpgradeFields) ProtoMessage() {} +func (*ModifiableUpgradeFields) Descriptor() ([]byte, []int) { + return fileDescriptor_fb1cef68588848b2, []int{1} +} +func (m *ModifiableUpgradeFields) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ModifiableUpgradeFields) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ModifiableUpgradeFields.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ModifiableUpgradeFields) XXX_Merge(src proto.Message) { + xxx_messageInfo_ModifiableUpgradeFields.Merge(m, src) +} +func (m *ModifiableUpgradeFields) XXX_Size() int { + return m.Size() +} +func (m *ModifiableUpgradeFields) XXX_DiscardUnknown() { + xxx_messageInfo_ModifiableUpgradeFields.DiscardUnknown(m) +} + +var xxx_messageInfo_ModifiableUpgradeFields proto.InternalMessageInfo + +func (m *ModifiableUpgradeFields) GetOrdering() Order { + if m != nil { + return m.Ordering + } + return NONE +} + +func (m *ModifiableUpgradeFields) GetConnectionHops() []string { + if m != nil { + return m.ConnectionHops + } + return nil +} + +func (m *ModifiableUpgradeFields) GetVersion() string { + if m != nil { + return m.Version + } + return "" +} + // UpgradeTimeout defines a type which encapsulates the upgrade timeout values at which the counterparty // must no longer proceed with the upgrade handshake. type UpgradeTimeout struct { @@ -37,7 +163,7 @@ func (m *UpgradeTimeout) Reset() { *m = UpgradeTimeout{} } func (m *UpgradeTimeout) String() string { return proto.CompactTextString(m) } func (*UpgradeTimeout) ProtoMessage() {} func (*UpgradeTimeout) Descriptor() ([]byte, []int) { - return fileDescriptor_fb1cef68588848b2, []int{0} + return fileDescriptor_fb1cef68588848b2, []int{2} } func (m *UpgradeTimeout) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -94,7 +220,7 @@ func (m *ErrorReceipt) Reset() { *m = ErrorReceipt{} } func (m *ErrorReceipt) String() string { return proto.CompactTextString(m) } func (*ErrorReceipt) ProtoMessage() {} func (*ErrorReceipt) Descriptor() ([]byte, []int) { - return fileDescriptor_fb1cef68588848b2, []int{1} + return fileDescriptor_fb1cef68588848b2, []int{3} } func (m *ErrorReceipt) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -138,6 +264,8 @@ func (m *ErrorReceipt) GetError() string { } func init() { + proto.RegisterType((*Upgrade)(nil), "ibc.core.channel.v1.Upgrade") + proto.RegisterType((*ModifiableUpgradeFields)(nil), "ibc.core.channel.v1.ModifiableUpgradeFields") proto.RegisterType((*UpgradeTimeout)(nil), "ibc.core.channel.v1.UpgradeTimeout") proto.RegisterType((*ErrorReceipt)(nil), "ibc.core.channel.v1.ErrorReceipt") } @@ -145,27 +273,129 @@ func init() { func init() { proto.RegisterFile("ibc/core/channel/v1/upgrade.proto", fileDescriptor_fb1cef68588848b2) } var fileDescriptor_fb1cef68588848b2 = []byte{ - // 307 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x44, 0x90, 0x4f, 0x4b, 0xc3, 0x30, - 0x18, 0x87, 0x1b, 0x99, 0xa2, 0x51, 0x87, 0xd6, 0x1d, 0x46, 0x0f, 0xdd, 0xdc, 0x69, 0x20, 0x4b, - 0x9c, 0x0a, 0xe2, 0x4d, 0x06, 0xa2, 0xe7, 0x3a, 0x2f, 0x5e, 0x64, 0xcd, 0x5e, 0xd2, 0xc0, 0xda, - 0xb7, 0x26, 0x69, 0xc1, 0x2f, 0xe0, 0xd9, 0x8f, 0xb5, 0xe3, 0x8e, 0x9e, 0x44, 0xb6, 0x2f, 0x22, - 0x6d, 0x36, 0x77, 0xca, 0xfb, 0xe7, 0xc9, 0xf3, 0xc2, 0x8f, 0x9e, 0xab, 0x58, 0x70, 0x81, 0x1a, - 0xb8, 0x48, 0x26, 0x59, 0x06, 0x33, 0x5e, 0x0e, 0x79, 0x91, 0x4b, 0x3d, 0x99, 0x02, 0xcb, 0x35, - 0x5a, 0xf4, 0xcf, 0x54, 0x2c, 0x58, 0x85, 0xb0, 0x35, 0xc2, 0xca, 0x61, 0xd0, 0x92, 0x28, 0xb1, - 0xde, 0xf3, 0xaa, 0x72, 0x68, 0xd0, 0xd9, 0xda, 0x66, 0x0a, 0x32, 0x5b, 0xc9, 0x5c, 0xe5, 0x80, - 0xde, 0x27, 0xa1, 0xcd, 0x17, 0x67, 0x1f, 0xab, 0x14, 0xb0, 0xb0, 0xfe, 0x23, 0x6d, 0x5a, 0x57, - 0xbe, 0x25, 0xa0, 0x64, 0x62, 0xdb, 0xa4, 0x4b, 0xfa, 0x87, 0x57, 0x01, 0xdb, 0xde, 0x75, 0x8a, - 0x72, 0xc8, 0x9e, 0x6a, 0x62, 0xd4, 0x98, 0xff, 0x74, 0xbc, 0xe8, 0x78, 0xfd, 0xcf, 0x0d, 0xfd, - 0x0b, 0x7a, 0xba, 0x11, 0x55, 0xaf, 0xb1, 0x93, 0x34, 0x6f, 0xef, 0x74, 0x49, 0xbf, 0x11, 0x9d, - 0xac, 0x17, 0xe3, 0xcd, 0xbc, 0x77, 0x4f, 0x8f, 0x1e, 0xb4, 0x46, 0x1d, 0x81, 0x00, 0x95, 0x5b, - 0x3f, 0xa0, 0xfb, 0x06, 0xde, 0x0b, 0xc8, 0x04, 0xd4, 0xf7, 0x1b, 0xd1, 0x7f, 0xef, 0xb7, 0xe8, - 0x2e, 0x54, 0x6c, 0x2d, 0x3b, 0x88, 0x5c, 0x33, 0x7a, 0x9e, 0x2f, 0x43, 0xb2, 0x58, 0x86, 0xe4, - 0x77, 0x19, 0x92, 0xaf, 0x55, 0xe8, 0x2d, 0x56, 0xa1, 0xf7, 0xbd, 0x0a, 0xbd, 0xd7, 0x3b, 0xa9, - 0x6c, 0x52, 0xc4, 0x4c, 0x60, 0xca, 0x05, 0x9a, 0x14, 0x0d, 0x57, 0xb1, 0x18, 0x48, 0xe4, 0xe5, - 0x2d, 0x4f, 0x71, 0x5a, 0xcc, 0xc0, 0xb8, 0x94, 0x2e, 0x6f, 0x06, 0x9b, 0xd8, 0xed, 0x47, 0x0e, - 0x26, 0xde, 0xab, 0x63, 0xba, 0xfe, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x11, 0x96, 0x44, 0x6d, 0x97, - 0x01, 0x00, 0x00, + // 479 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x31, 0x6f, 0xd4, 0x30, + 0x14, 0xc7, 0x2f, 0xed, 0xc1, 0xf5, 0x0c, 0x84, 0x62, 0x2a, 0x11, 0xdd, 0x90, 0x1e, 0xc7, 0xc0, + 0x49, 0xd0, 0x84, 0x2b, 0x08, 0xc4, 0x86, 0x8a, 0x80, 0x2e, 0x08, 0x94, 0x96, 0x01, 0x96, 0x28, + 0x71, 0x5e, 0x13, 0x8b, 0xc4, 0x2f, 0xc4, 0x4e, 0x24, 0xbe, 0x00, 0x33, 0x03, 0x1f, 0xaa, 0x63, + 0x25, 0x16, 0x26, 0x84, 0xee, 0xbe, 0x08, 0x8a, 0xe3, 0xb4, 0x45, 0x3a, 0xa6, 0xd8, 0x7f, 0xff, + 0xde, 0xdf, 0x7f, 0xbf, 0x3c, 0x72, 0x97, 0xc7, 0xcc, 0x67, 0x58, 0x81, 0xcf, 0xb2, 0x48, 0x08, + 0xc8, 0xfd, 0x66, 0xe1, 0xd7, 0x65, 0x5a, 0x45, 0x09, 0x78, 0x65, 0x85, 0x0a, 0xe9, 0x6d, 0x1e, + 0x33, 0xaf, 0x45, 0x3c, 0x83, 0x78, 0xcd, 0x62, 0xb2, 0x93, 0x62, 0x8a, 0xfa, 0xdc, 0x6f, 0x57, + 0x1d, 0x3a, 0xd9, 0xbd, 0x70, 0xcb, 0x39, 0x08, 0xd5, 0x9a, 0x75, 0x2b, 0x03, 0xac, 0xbd, 0xae, + 0xb7, 0xd5, 0xc8, 0xec, 0xa7, 0x45, 0x46, 0x1f, 0xba, 0x00, 0xf4, 0x23, 0xb1, 0x4d, 0x96, 0xf0, + 0x84, 0x43, 0x9e, 0x48, 0xc7, 0x9a, 0x5a, 0xf3, 0x6b, 0xfb, 0x0f, 0xbd, 0x35, 0x99, 0xbc, 0xb7, + 0x98, 0xf0, 0x13, 0x1e, 0xc5, 0x39, 0x98, 0xfa, 0xd7, 0xba, 0xe6, 0x60, 0x78, 0xfa, 0x7b, 0x77, + 0x10, 0xdc, 0xa8, 0x2f, 0x8b, 0xf4, 0x25, 0x19, 0x29, 0x5e, 0x00, 0xd6, 0xca, 0xd9, 0xd0, 0x9e, + 0xf7, 0xd6, 0x7a, 0x1a, 0xa7, 0xe3, 0x0e, 0x35, 0x56, 0x7d, 0x25, 0x9d, 0x93, 0xed, 0x3c, 0x92, + 0x2a, 0x2c, 0x23, 0xf6, 0x19, 0x54, 0x28, 0x41, 0x28, 0x67, 0x73, 0x6a, 0xcd, 0x87, 0x81, 0xdd, + 0xea, 0xef, 0xb5, 0x7c, 0x04, 0x42, 0xcd, 0x7e, 0x58, 0xe4, 0xce, 0x7f, 0xf2, 0xd1, 0xa7, 0x64, + 0x0b, 0xab, 0x04, 0x2a, 0x2e, 0x52, 0xfd, 0x3e, 0x7b, 0x7f, 0xb2, 0x36, 0xcb, 0xbb, 0x16, 0x0a, + 0xce, 0x59, 0x7a, 0x9f, 0xdc, 0x64, 0x28, 0x04, 0x30, 0xc5, 0x51, 0x84, 0x19, 0x96, 0xd2, 0xd9, + 0x98, 0x6e, 0xce, 0xc7, 0x81, 0x7d, 0x21, 0x1f, 0x62, 0x29, 0xa9, 0x43, 0x46, 0x0d, 0x54, 0x92, + 0xa3, 0xd0, 0xe9, 0xc6, 0x41, 0xbf, 0x9d, 0x7d, 0xb3, 0x88, 0xfd, 0xef, 0x13, 0xe9, 0x1b, 0x62, + 0x9b, 0xe7, 0x85, 0x19, 0xf0, 0x34, 0x53, 0xa6, 0xe7, 0x97, 0x33, 0x75, 0xbf, 0xb4, 0x59, 0x78, + 0x87, 0x9a, 0xe8, 0x3b, 0x6c, 0xea, 0x3a, 0x91, 0x3e, 0x20, 0xb7, 0x7a, 0xa3, 0xf6, 0x2b, 0x55, + 0x54, 0x94, 0xba, 0xd7, 0xc3, 0x60, 0xdb, 0x1c, 0x1c, 0xf7, 0xfa, 0xec, 0x05, 0xb9, 0xfe, 0xaa, + 0xaa, 0xb0, 0x0a, 0x80, 0x01, 0x2f, 0x15, 0x9d, 0x90, 0x2d, 0x09, 0x5f, 0x6a, 0x10, 0x0c, 0xf4, + 0xfd, 0xc3, 0xe0, 0x7c, 0x4f, 0x77, 0xc8, 0x15, 0x68, 0x59, 0x6d, 0x36, 0x0e, 0xba, 0xcd, 0xc1, + 0xd1, 0xe9, 0xd2, 0xb5, 0xce, 0x96, 0xae, 0xf5, 0x67, 0xe9, 0x5a, 0xdf, 0x57, 0xee, 0xe0, 0x6c, + 0xe5, 0x0e, 0x7e, 0xad, 0xdc, 0xc1, 0xa7, 0xe7, 0x29, 0x57, 0x59, 0x1d, 0x7b, 0x0c, 0x0b, 0x9f, + 0xa1, 0x2c, 0x50, 0xfa, 0x3c, 0x66, 0x7b, 0x29, 0xfa, 0xcd, 0x33, 0xbf, 0xc0, 0xa4, 0xce, 0x41, + 0x76, 0x43, 0xf9, 0xe8, 0xc9, 0x5e, 0x3f, 0x97, 0xea, 0x6b, 0x09, 0x32, 0xbe, 0xaa, 0x67, 0xf2, + 0xf1, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa8, 0x15, 0x49, 0xab, 0x27, 0x03, 0x00, 0x00, +} + +func (m *Upgrade) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Upgrade) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Upgrade) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.LastPacketSent != 0 { + i = encodeVarintUpgrade(dAtA, i, uint64(m.LastPacketSent)) + i-- + dAtA[i] = 0x18 + } + { + size, err := m.Timeout.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUpgrade(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.UpgradeFields.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUpgrade(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *ModifiableUpgradeFields) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ModifiableUpgradeFields) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ModifiableUpgradeFields) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = encodeVarintUpgrade(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0x1a + } + if len(m.ConnectionHops) > 0 { + for iNdEx := len(m.ConnectionHops) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ConnectionHops[iNdEx]) + copy(dAtA[i:], m.ConnectionHops[iNdEx]) + i = encodeVarintUpgrade(dAtA, i, uint64(len(m.ConnectionHops[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if m.Ordering != 0 { + i = encodeVarintUpgrade(dAtA, i, uint64(m.Ordering)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil } func (m *UpgradeTimeout) Marshal() (dAtA []byte, err error) { @@ -252,6 +482,44 @@ func encodeVarintUpgrade(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } +func (m *Upgrade) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.UpgradeFields.Size() + n += 1 + l + sovUpgrade(uint64(l)) + l = m.Timeout.Size() + n += 1 + l + sovUpgrade(uint64(l)) + if m.LastPacketSent != 0 { + n += 1 + sovUpgrade(uint64(m.LastPacketSent)) + } + return n +} + +func (m *ModifiableUpgradeFields) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Ordering != 0 { + n += 1 + sovUpgrade(uint64(m.Ordering)) + } + if len(m.ConnectionHops) > 0 { + for _, s := range m.ConnectionHops { + l = len(s) + n += 1 + l + sovUpgrade(uint64(l)) + } + } + l = len(m.Version) + if l > 0 { + n += 1 + l + sovUpgrade(uint64(l)) + } + return n +} + func (m *UpgradeTimeout) Size() (n int) { if m == nil { return 0 @@ -288,6 +556,274 @@ func sovUpgrade(x uint64) (n int) { func sozUpgrade(x uint64) (n int) { return sovUpgrade(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } +func (m *Upgrade) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpgrade + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Upgrade: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Upgrade: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpgradeFields", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpgrade + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUpgrade + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUpgrade + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.UpgradeFields.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpgrade + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUpgrade + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUpgrade + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Timeout.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LastPacketSent", wireType) + } + m.LastPacketSent = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpgrade + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.LastPacketSent |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipUpgrade(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUpgrade + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ModifiableUpgradeFields) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpgrade + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ModifiableUpgradeFields: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ModifiableUpgradeFields: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ordering", wireType) + } + m.Ordering = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpgrade + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Ordering |= Order(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionHops", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpgrade + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthUpgrade + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUpgrade + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConnectionHops = append(m.ConnectionHops, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpgrade + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthUpgrade + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUpgrade + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Version = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUpgrade(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUpgrade + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *UpgradeTimeout) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/proto/ibc/core/channel/v1/upgrade.proto b/proto/ibc/core/channel/v1/upgrade.proto index a846ca39ea8..03e63a0d2c1 100644 --- a/proto/ibc/core/channel/v1/upgrade.proto +++ b/proto/ibc/core/channel/v1/upgrade.proto @@ -6,6 +6,25 @@ option go_package = "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"; import "gogoproto/gogo.proto"; import "ibc/core/client/v1/client.proto"; +import "ibc/core/channel/v1/channel.proto"; + +// Upgrade is a verifiable type which contains the relevant information +// for an attempted upgrade. It provides the proposed changes to the channel +// end, the timeout for this upgrade attempt and the last packet sequence sent +// to allow the counterparty to block sends after the upgrade has started. +message Upgrade { + ModifiableUpgradeFields upgrade_fields = 1 [(gogoproto.nullable) = false]; + UpgradeTimeout timeout = 2 [(gogoproto.nullable) = false]; + uint64 last_packet_sent = 3; +} + +// ModifiableUpgradeFields are the fields in a channel end which may be changed +// during a channel upgrade. +message ModifiableUpgradeFields { + Order ordering = 1; + repeated string connection_hops = 2; + string version = 3; +} // UpgradeTimeout defines a type which encapsulates the upgrade timeout values at which the counterparty // must no longer proceed with the upgrade handshake. From f8f6ad461aae3e6cf2350d8ca2e8fe950668bbc9 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 13 Apr 2023 11:19:27 +0100 Subject: [PATCH 02/18] added unit test for CheckIsOpen --- .../core/03-connection/keeper/keeper_test.go | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/modules/core/03-connection/keeper/keeper_test.go b/modules/core/03-connection/keeper/keeper_test.go index dbae331d690..2d0b42dbab9 100644 --- a/modules/core/03-connection/keeper/keeper_test.go +++ b/modules/core/03-connection/keeper/keeper_test.go @@ -8,6 +8,7 @@ import ( "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" "github.com/cosmos/ibc-go/v7/modules/core/exported" ibctesting "github.com/cosmos/ibc-go/v7/testing" ) @@ -176,3 +177,64 @@ func (suite *KeeperTestSuite) TestLocalhostConnectionEndCreation() { expectedCounterParty := types.NewCounterparty(exported.LocalhostClientID, exported.LocalhostConnectionID, commitmenttypes.NewMerklePrefix(connectionKeeper.GetCommitmentPrefix().Bytes())) suite.Require().Equal(expectedCounterParty, connectionEnd.Counterparty) } + +func (suite *KeeperTestSuite) TestCheckIsOpen() { + var path *ibctesting.Path + + cases := []struct { + msg string + malleate func() + expPass bool + }{ + { + msg: "success", + malleate: func() {}, + expPass: true, + }, + { + msg: "uninitialized connection", + malleate: func() { + connection := path.EndpointA.GetConnection() + connection.State = types.UNINITIALIZED + path.EndpointA.SetConnection(connection) + }, + expPass: false, + }, + { + msg: "init connection", + malleate: func() { + connection := path.EndpointA.GetConnection() + connection.State = types.INIT + path.EndpointA.SetConnection(connection) + }, + expPass: false, + }, + { + msg: "no connection", + malleate: func() { + storeKey := suite.chainA.GetSimApp().GetKey(exported.StoreKey) + kvStore := suite.chainA.GetContext().KVStore(storeKey) + kvStore.Delete(host.ConnectionKey(ibctesting.FirstConnectionID)) + }, + expPass: false, + }, + } + + for _, tc := range cases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + path = ibctesting.NewPath(suite.chainA, suite.chainB) + suite.coordinator.Setup(path) + + tc.malleate() + + connectionKeeper := suite.chainA.GetSimApp().IBCKeeper.ConnectionKeeper + + if tc.expPass { + suite.Require().NoError(connectionKeeper.CheckIsOpen(suite.chainA.GetContext(), ibctesting.FirstConnectionID)) + } else { + suite.Require().Error(connectionKeeper.CheckIsOpen(suite.chainA.GetContext(), ibctesting.FirstConnectionID)) + } + }) + } +} From 808ec708f4fcf02aacc4162d5c4a4efef65023c0 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 13 Apr 2023 11:55:40 +0100 Subject: [PATCH 03/18] adding unit tests for ValidateProposedUpgradeFields --- modules/core/04-channel/keeper/keeper.go | 26 ++++++ modules/core/04-channel/keeper/keeper_test.go | 89 +++++++++++++++++++ modules/core/04-channel/keeper/upgrade.go | 24 ----- 3 files changed, 115 insertions(+), 24 deletions(-) diff --git a/modules/core/04-channel/keeper/keeper.go b/modules/core/04-channel/keeper/keeper.go index c707be3a09d..46a829b5c50 100644 --- a/modules/core/04-channel/keeper/keeper.go +++ b/modules/core/04-channel/keeper/keeper.go @@ -1,6 +1,7 @@ package keeper import ( + "reflect" "strconv" "strings" @@ -571,6 +572,31 @@ func (k Keeper) DeleteUpgradeTimeout(ctx sdk.Context, portID, channelID string) store.Delete(host.ChannelUpgradeTimeoutKey(portID, channelID)) } +// ValidateProposedUpgradeFields validates the proposed upgrade fields against the existing channel. +// It returns an error if the following constraints are not met: +// - there exists at least one valid proposed change to the existing channel fields +// - the proposed order is a subset of the existing order +// - the proposed connection hops do not exist +// - the proposed version is non-empty (checked in ModifiableUpgradeFields.ValidateBasic()) +// - the proposed connection hops are not open +func (k Keeper) ValidateProposedUpgradeFields(ctx sdk.Context, proposedUpgrade types.ModifiableUpgradeFields, existingChannel types.Channel) error { + currentFields := types.ModifiableUpgradeFields{ + Ordering: existingChannel.Ordering, + ConnectionHops: existingChannel.ConnectionHops, + Version: existingChannel.Version, + } + if reflect.DeepEqual(proposedUpgrade, currentFields) { + return errorsmod.Wrap(types.ErrChannelExists, "existing channel end is identical to proposed upgrade channel end") + } + + if !currentFields.Ordering.SubsetOf(proposedUpgrade.Ordering) { + return errorsmod.Wrap(types.ErrInvalidChannelOrdering, "channel ordering must be a subset of the new ordering") + } + + return k.connectionKeeper.CheckIsOpen(ctx, proposedUpgrade.ConnectionHops[0]) +} + + // common functionality for IteratePacketCommitment and IteratePacketAcknowledgement func (k Keeper) iterateHashes(ctx sdk.Context, iterator db.Iterator, cb func(portID, channelID string, sequence uint64, hash []byte) bool) { defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) diff --git a/modules/core/04-channel/keeper/keeper_test.go b/modules/core/04-channel/keeper/keeper_test.go index 8de199416b5..aeac2882e2a 100644 --- a/modules/core/04-channel/keeper/keeper_test.go +++ b/modules/core/04-channel/keeper/keeper_test.go @@ -8,7 +8,10 @@ import ( transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ibctesting "github.com/cosmos/ibc-go/v7/testing" ibcmock "github.com/cosmos/ibc-go/v7/testing/mock" ) @@ -557,3 +560,89 @@ func (suite *KeeperTestSuite) TestUpgradeTimeoutAccessors() { suite.Require().False(found) }) } + +func (suite *KeeperTestSuite) TestValidateProposedUpgradeFields() { + + var ( + proposedUpgrade *types.ModifiableUpgradeFields + path *ibctesting.Path + ) + + tests := []struct { + name string + malleate func() + expPass bool + }{ + { + name: "modify ordering to invalid value", + malleate: func() { + proposedUpgrade.Ordering = types.ORDERED + }, + expPass: false, + }, + { + name: "no modified fields in proposed upgrade", + malleate: func() {}, + expPass: false, + }, + { + name: "change channel version", + malleate: func() { + proposedUpgrade.Version = "1.0.0" + }, + expPass: true, + }, + { + name: "change connection hops", + malleate: func() { + path := ibctesting.NewPath(suite.chainA, suite.chainB) + suite.coordinator.Setup(path) + proposedUpgrade.ConnectionHops = []string{path.EndpointA.ConnectionID} + }, + expPass: true, + }, + { + name: "attempt upgrade when connection is not set", + malleate: func() { + storeKey := suite.chainA.GetSimApp().GetKey(exported.StoreKey) + kvStore := suite.chainA.GetContext().KVStore(storeKey) + kvStore.Delete(host.ConnectionKey(ibctesting.FirstConnectionID)) + }, + expPass: false, + }, + { + name: "attempt upgrade when connection is not open", + malleate: func() { + connection := path.EndpointA.GetConnection() + connection.State = connectiontypes.UNINITIALIZED + path.EndpointA.SetConnection(connection) + }, + expPass: false, + }, + } + + for _, tc := range tests { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + path = ibctesting.NewPath(suite.chainA, suite.chainB) + suite.coordinator.Setup(path) + + existingChannel := path.EndpointA.GetChannel() + proposedUpgrade = &types.ModifiableUpgradeFields{ + Ordering: existingChannel.Ordering, + ConnectionHops: existingChannel.ConnectionHops, + Version: existingChannel.Version, + } + + tc.malleate() + + err := suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.ValidateProposedUpgradeFields(suite.chainA.GetContext(), *proposedUpgrade, existingChannel) + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/modules/core/04-channel/keeper/upgrade.go b/modules/core/04-channel/keeper/upgrade.go index c08e059321b..90537cc7a4c 100644 --- a/modules/core/04-channel/keeper/upgrade.go +++ b/modules/core/04-channel/keeper/upgrade.go @@ -134,27 +134,3 @@ func (k Keeper) RestoreChannel(ctx sdk.Context, portID, channelID string, upgrad return cbs.OnChanUpgradeRestore(ctx, portID, channelID) } - -// validateProposedUpgradeFields validates the proposed upgrade fields against the existing channel. -// It returns an error if the following constraints are not met: -// - there exists at least one valid proposed change to the existing channel fields -// - the proposed order is a subset of the existing order -// - the proposed connection hops do not exist -// - the proposed version is non-empty (checked in ModifiableUpgradeFields.ValidateBasic()) -// - the proposed connection hops are not open -func (k Keeper) validateProposedUpgradeFields(ctx sdk.Context, proposedUpgrade types.ModifiableUpgradeFields, existingChannel types.Channel) error { - currentFields := types.ModifiableUpgradeFields{ - Ordering: existingChannel.Ordering, - ConnectionHops: existingChannel.ConnectionHops, - Version: existingChannel.Version, - } - if reflect.DeepEqual(proposedUpgrade, currentFields) { - return errorsmod.Wrap(types.ErrChannelExists, "existing channel end is identical to proposed upgrade channel end") - } - - if !currentFields.Ordering.SubsetOf(proposedUpgrade.Ordering) { - return errorsmod.Wrap(types.ErrInvalidChannelOrdering, "channel ordering must be a subset of the new ordering") - } - - return k.connectionKeeper.CheckIsOpen(ctx, proposedUpgrade.ConnectionHops[0]) -} From 8dd0c44369f64229e5f0d08df8b2780b799579a6 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 13 Apr 2023 11:58:59 +0100 Subject: [PATCH 04/18] fix linter --- modules/core/04-channel/keeper/keeper.go | 1 - modules/core/04-channel/keeper/keeper_test.go | 1 - 2 files changed, 2 deletions(-) diff --git a/modules/core/04-channel/keeper/keeper.go b/modules/core/04-channel/keeper/keeper.go index 46a829b5c50..12841d8050a 100644 --- a/modules/core/04-channel/keeper/keeper.go +++ b/modules/core/04-channel/keeper/keeper.go @@ -596,7 +596,6 @@ func (k Keeper) ValidateProposedUpgradeFields(ctx sdk.Context, proposedUpgrade t return k.connectionKeeper.CheckIsOpen(ctx, proposedUpgrade.ConnectionHops[0]) } - // common functionality for IteratePacketCommitment and IteratePacketAcknowledgement func (k Keeper) iterateHashes(ctx sdk.Context, iterator db.Iterator, cb func(portID, channelID string, sequence uint64, hash []byte) bool) { defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) diff --git a/modules/core/04-channel/keeper/keeper_test.go b/modules/core/04-channel/keeper/keeper_test.go index aeac2882e2a..082d51af6d2 100644 --- a/modules/core/04-channel/keeper/keeper_test.go +++ b/modules/core/04-channel/keeper/keeper_test.go @@ -562,7 +562,6 @@ func (suite *KeeperTestSuite) TestUpgradeTimeoutAccessors() { } func (suite *KeeperTestSuite) TestValidateProposedUpgradeFields() { - var ( proposedUpgrade *types.ModifiableUpgradeFields path *ibctesting.Path From 64878aee84e97f2f6af722459b2e868b647da5ab Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 13 Apr 2023 12:02:30 +0100 Subject: [PATCH 05/18] update to use errorsmod instead of sdk errors --- modules/core/04-channel/keeper/keeper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/04-channel/keeper/keeper.go b/modules/core/04-channel/keeper/keeper.go index 12841d8050a..023c81915f1 100644 --- a/modules/core/04-channel/keeper/keeper.go +++ b/modules/core/04-channel/keeper/keeper.go @@ -5,12 +5,12 @@ import ( "strconv" "strings" + errorsmod "cosmossdk.io/errors" db "github.com/cometbft/cometbft-db" "github.com/cometbft/cometbft/libs/log" "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - errorsmod "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" From 51bb7a7709ce38482c6fc5d13bb91d814952e14c Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 13 Apr 2023 14:27:27 +0100 Subject: [PATCH 06/18] added upgrade verification function and unit tests --- modules/core/03-connection/keeper/verify.go | 42 ++++++++++ .../core/03-connection/keeper/verify_test.go | 79 +++++++++++++++++++ modules/core/04-channel/keeper/keeper.go | 21 +++++ modules/core/04-channel/types/upgrade.go | 27 +++++++ modules/core/24-host/channel_keys.go | 10 +++ 5 files changed, 179 insertions(+) diff --git a/modules/core/03-connection/keeper/verify.go b/modules/core/03-connection/keeper/verify.go index 62ff9dfce52..f8afcf74db7 100644 --- a/modules/core/03-connection/keeper/verify.go +++ b/modules/core/03-connection/keeper/verify.go @@ -523,6 +523,48 @@ func (k Keeper) VerifyChannelUpgradeErrorAbsence( return nil } +// VerifyChannelUpgrade verifies the proof that a particular proposed upgrade has been stored in the upgrade path. +func (k Keeper) VerifyChannelUpgrade( + ctx sdk.Context, + connection exported.ConnectionI, + height exported.Height, + proof []byte, + portID, + channelID string, + upgrade channeltypes.Upgrade, +) error { + clientID := connection.GetClientID() + clientState, clientStore, err := k.getClientStateAndVerificationStore(ctx, clientID) + if err != nil { + return err + } + + if status := k.clientKeeper.GetClientStatus(ctx, clientState, clientID); status != exported.Active { + return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + } + + merklePath := commitmenttypes.NewMerklePath(host.ChannelUpgradePath(portID, channelID)) + merklePath, err = commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) + if err != nil { + return err + } + + bz, err := k.cdc.Marshal(&upgrade) + if err != nil { + return err + } + + if err := clientState.VerifyMembership( + ctx, clientStore, k.cdc, height, + 0, 0, // skip delay period checks for non-packet processing verification + proof, merklePath, bz, + ); err != nil { + return errorsmod.Wrapf(err, "failed upgrade verification for client (%s) on channel (%s)", clientID, channelID) + } + + return nil +} + // getBlockDelay calculates the block delay period from the time delay of the connection // and the maximum expected time per block. func (k Keeper) getBlockDelay(ctx sdk.Context, connection exported.ConnectionI) uint64 { diff --git a/modules/core/03-connection/keeper/verify_test.go b/modules/core/03-connection/keeper/verify_test.go index 64e265559b1..887b4223cdf 100644 --- a/modules/core/03-connection/keeper/verify_test.go +++ b/modules/core/03-connection/keeper/verify_test.go @@ -1020,6 +1020,85 @@ func (suite *KeeperTestSuite) TestVerifyUpgradeErrorReceiptAbsence() { } } +func (suite *KeeperTestSuite) TestVerifyUpgrade() { + var ( + path *ibctesting.Path + upgrade *channeltypes.Upgrade + ) + + cases := []struct { + name string + malleate func() + expPass bool + }{ + { + name: "success", + malleate: func() {}, + expPass: true, + }, + { + name: "fails when client state is frozen", + malleate: func() { + clientState := path.EndpointB.GetClientState().(*ibctm.ClientState) + clientState.FrozenHeight = clienttypes.NewHeight(0, 1) + path.EndpointB.SetClientState(clientState) + }, + expPass: false, + }, + { + name: "fails with bad client id", + malleate: func() { + connection := path.EndpointB.GetConnection() + connection.ClientId = ibctesting.InvalidID + path.EndpointB.SetConnection(connection) + }, + expPass: false, + }, + { + name: "verification fails when the ordering is different", + malleate: func() { + upgrade.UpgradeFields.Ordering = channeltypes.ORDERED + }, + expPass: false, + }, + } + + for _, tc := range cases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + path = ibctesting.NewPath(suite.chainA, suite.chainB) + suite.coordinator.Setup(path) + + upgrade = channeltypes.NewUpgrade( + channeltypes.NewModifiableUpgradeFields(channeltypes.UNORDERED, []string{path.EndpointA.ConnectionID}, "v1.0.0"), + channeltypes.NewUpgradeTimeout(clienttypes.ZeroHeight(), 100000), + 0, + ) + + suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.SetUpgrade(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, *upgrade) + + suite.chainA.Coordinator.CommitBlock(suite.chainA) + suite.Require().NoError(path.EndpointB.UpdateClient()) + + tc.malleate() + + upgradeErrorReceiptKey := host.ChannelUpgradeKey(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) + proof, proofHeight := suite.chainA.QueryProof(upgradeErrorReceiptKey) + + err := suite.chainB.GetSimApp().IBCKeeper.ConnectionKeeper.VerifyChannelUpgrade(suite.chainB.GetContext(), path.EndpointB.GetConnection(), proofHeight, proof, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, *upgrade) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + func malleateHeight(height exported.Height, diff uint64) exported.Height { return clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+diff) } diff --git a/modules/core/04-channel/keeper/keeper.go b/modules/core/04-channel/keeper/keeper.go index 023c81915f1..b2b36ba02fd 100644 --- a/modules/core/04-channel/keeper/keeper.go +++ b/modules/core/04-channel/keeper/keeper.go @@ -572,6 +572,27 @@ func (k Keeper) DeleteUpgradeTimeout(ctx sdk.Context, portID, channelID string) store.Delete(host.ChannelUpgradeTimeoutKey(portID, channelID)) } +// GetUpgrade returns the proposed upgrade for the provided port and channel identifers. +func (k Keeper) GetUpgrade(ctx sdk.Context, portID, channelID string) (types.Upgrade, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(host.ChannelUpgradeKey(portID, channelID)) + if bz == nil { + return types.Upgrade{}, false + } + + var upgrade types.Upgrade + k.cdc.MustUnmarshal(bz, &upgrade) + + return upgrade, true +} + +// SetUpgrade sets the proposed upgrade using the provided port and channel identifiers. +func (k Keeper) SetUpgrade(ctx sdk.Context, portID, channelID string, upgrade types.Upgrade) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshal(&upgrade) + store.Set(host.ChannelUpgradeKey(portID, channelID), bz) +} + // ValidateProposedUpgradeFields validates the proposed upgrade fields against the existing channel. // It returns an error if the following constraints are not met: // - there exists at least one valid proposed change to the existing channel fields diff --git a/modules/core/04-channel/types/upgrade.go b/modules/core/04-channel/types/upgrade.go index 8daed670bfe..2a25deec484 100644 --- a/modules/core/04-channel/types/upgrade.go +++ b/modules/core/04-channel/types/upgrade.go @@ -4,8 +4,35 @@ import ( errorsmod "cosmossdk.io/errors" "github.com/cosmos/ibc-go/v7/internal/collections" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ) +// NewUpgrade creates a new Upgrade instance. +func NewUpgrade(modifiableFields ModifiableUpgradeFields, timeout UpgradeTimeout, lastPacketSent uint64) *Upgrade { + return &Upgrade{ + UpgradeFields: modifiableFields, + Timeout: timeout, + LastPacketSent: lastPacketSent, + } +} + +// NewModifiableUpgradeFields returns a new ModifiableUpgradeFields instance. +func NewModifiableUpgradeFields(ordering Order, connectionHops []string, version string) ModifiableUpgradeFields { + return ModifiableUpgradeFields{ + Ordering: ordering, + ConnectionHops: connectionHops, + Version: version, + } +} + +// NewUpgradeTimeout returns a new UpgradeTimeout instance. +func NewUpgradeTimeout(height clienttypes.Height, timestamp uint64) UpgradeTimeout { + return UpgradeTimeout{ + TimeoutHeight: height, + TimeoutTimestamp: timestamp, + } +} + // ValidateBasic performs a basic validation of the upgrade fields func (u Upgrade) ValidateBasic() error { if err := u.UpgradeFields.ValidateBasic(); err != nil { diff --git a/modules/core/24-host/channel_keys.go b/modules/core/24-host/channel_keys.go index 3158cd9cdfc..015ac6e60ca 100644 --- a/modules/core/24-host/channel_keys.go +++ b/modules/core/24-host/channel_keys.go @@ -71,6 +71,16 @@ func ChannelUpgradeSequenceKey(portID, channelID string) []byte { return []byte(ChannelUpgradeSequencePath(portID, channelID)) } +// ChannelUpgradePath defines the path which stores the information related to an upgrade attempt +func ChannelUpgradePath(portID, channelID string) string { + return fmt.Sprintf("%s/%s", KeyChannelUpgradePrefix, channelPath(portID, channelID)) +} + +// ChannelUpgradeKey returns the store key for a particular channel upgrade attempt +func ChannelUpgradeKey(portID, channelID string) []byte { + return []byte(ChannelUpgradePath(portID, channelID)) +} + func channelPath(portID, channelID string) string { return fmt.Sprintf("%s/%s/%s/%s", KeyPortPrefix, portID, KeyChannelPrefix, channelID) } From f203df9ac1c2d4d043f54a6dd3a13f2fe20ebf86 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 13 Apr 2023 14:31:19 +0100 Subject: [PATCH 07/18] improved variable naming --- modules/core/03-connection/keeper/verify_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/03-connection/keeper/verify_test.go b/modules/core/03-connection/keeper/verify_test.go index 887b4223cdf..5916208691f 100644 --- a/modules/core/03-connection/keeper/verify_test.go +++ b/modules/core/03-connection/keeper/verify_test.go @@ -1085,8 +1085,8 @@ func (suite *KeeperTestSuite) TestVerifyUpgrade() { tc.malleate() - upgradeErrorReceiptKey := host.ChannelUpgradeKey(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - proof, proofHeight := suite.chainA.QueryProof(upgradeErrorReceiptKey) + channelUpgradeKey := host.ChannelUpgradeKey(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) + proof, proofHeight := suite.chainA.QueryProof(channelUpgradeKey) err := suite.chainB.GetSimApp().IBCKeeper.ConnectionKeeper.VerifyChannelUpgrade(suite.chainB.GetContext(), path.EndpointB.GetConnection(), proofHeight, proof, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, *upgrade) From 5f9ad6793b2b566b535b80462b0dac711cb62f41 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 13 Apr 2023 14:35:45 +0100 Subject: [PATCH 08/18] re-arranged order of test functions --- modules/core/04-channel/keeper/keeper_test.go | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/core/04-channel/keeper/keeper_test.go b/modules/core/04-channel/keeper/keeper_test.go index 082d51af6d2..daf2dfc0590 100644 --- a/modules/core/04-channel/keeper/keeper_test.go +++ b/modules/core/04-channel/keeper/keeper_test.go @@ -572,18 +572,6 @@ func (suite *KeeperTestSuite) TestValidateProposedUpgradeFields() { malleate func() expPass bool }{ - { - name: "modify ordering to invalid value", - malleate: func() { - proposedUpgrade.Ordering = types.ORDERED - }, - expPass: false, - }, - { - name: "no modified fields in proposed upgrade", - malleate: func() {}, - expPass: false, - }, { name: "change channel version", malleate: func() { @@ -600,6 +588,18 @@ func (suite *KeeperTestSuite) TestValidateProposedUpgradeFields() { }, expPass: true, }, + { + name: "modify ordering to invalid value", + malleate: func() { + proposedUpgrade.Ordering = types.ORDERED + }, + expPass: false, + }, + { + name: "no modified fields in proposed upgrade", + malleate: func() {}, + expPass: false, + }, { name: "attempt upgrade when connection is not set", malleate: func() { From 04dd0aff9245f3522630b555420329051f6ff7f2 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 13 Apr 2023 16:40:42 +0100 Subject: [PATCH 09/18] addressing PR feedback --- modules/core/04-channel/keeper/keeper.go | 6 +- modules/core/04-channel/keeper/keeper_test.go | 6 +- modules/core/04-channel/types/upgrade.go | 6 +- modules/core/04-channel/types/upgrade.pb.go | 122 +++++++++--------- proto/ibc/core/channel/v1/upgrade.proto | 8 +- 5 files changed, 75 insertions(+), 73 deletions(-) diff --git a/modules/core/04-channel/keeper/keeper.go b/modules/core/04-channel/keeper/keeper.go index 023c81915f1..b8f30f89c59 100644 --- a/modules/core/04-channel/keeper/keeper.go +++ b/modules/core/04-channel/keeper/keeper.go @@ -572,15 +572,15 @@ func (k Keeper) DeleteUpgradeTimeout(ctx sdk.Context, portID, channelID string) store.Delete(host.ChannelUpgradeTimeoutKey(portID, channelID)) } -// ValidateProposedUpgradeFields validates the proposed upgrade fields against the existing channel. +// ValidateUpgradeFields validates the proposed upgrade fields against the existing channel. // It returns an error if the following constraints are not met: // - there exists at least one valid proposed change to the existing channel fields // - the proposed order is a subset of the existing order // - the proposed connection hops do not exist // - the proposed version is non-empty (checked in ModifiableUpgradeFields.ValidateBasic()) // - the proposed connection hops are not open -func (k Keeper) ValidateProposedUpgradeFields(ctx sdk.Context, proposedUpgrade types.ModifiableUpgradeFields, existingChannel types.Channel) error { - currentFields := types.ModifiableUpgradeFields{ +func (k Keeper) ValidateUpgradeFields(ctx sdk.Context, proposedUpgrade types.UpgradeFields, existingChannel types.Channel) error { + currentFields := types.UpgradeFields{ Ordering: existingChannel.Ordering, ConnectionHops: existingChannel.ConnectionHops, Version: existingChannel.Version, diff --git a/modules/core/04-channel/keeper/keeper_test.go b/modules/core/04-channel/keeper/keeper_test.go index daf2dfc0590..8af0c1c2c48 100644 --- a/modules/core/04-channel/keeper/keeper_test.go +++ b/modules/core/04-channel/keeper/keeper_test.go @@ -563,7 +563,7 @@ func (suite *KeeperTestSuite) TestUpgradeTimeoutAccessors() { func (suite *KeeperTestSuite) TestValidateProposedUpgradeFields() { var ( - proposedUpgrade *types.ModifiableUpgradeFields + proposedUpgrade *types.UpgradeFields path *ibctesting.Path ) @@ -628,7 +628,7 @@ func (suite *KeeperTestSuite) TestValidateProposedUpgradeFields() { suite.coordinator.Setup(path) existingChannel := path.EndpointA.GetChannel() - proposedUpgrade = &types.ModifiableUpgradeFields{ + proposedUpgrade = &types.UpgradeFields{ Ordering: existingChannel.Ordering, ConnectionHops: existingChannel.ConnectionHops, Version: existingChannel.Version, @@ -636,7 +636,7 @@ func (suite *KeeperTestSuite) TestValidateProposedUpgradeFields() { tc.malleate() - err := suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.ValidateProposedUpgradeFields(suite.chainA.GetContext(), *proposedUpgrade, existingChannel) + err := suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.ValidateUpgradeFields(suite.chainA.GetContext(), *proposedUpgrade, existingChannel) if tc.expPass { suite.Require().NoError(err) } else { diff --git a/modules/core/04-channel/types/upgrade.go b/modules/core/04-channel/types/upgrade.go index 8daed670bfe..5673543f79f 100644 --- a/modules/core/04-channel/types/upgrade.go +++ b/modules/core/04-channel/types/upgrade.go @@ -1,6 +1,8 @@ package types import ( + "strings" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/ibc-go/v7/internal/collections" @@ -21,7 +23,7 @@ func (u Upgrade) ValidateBasic() error { } // ValidateBasic performs a basic validation of the proposed upgrade fields -func (muf ModifiableUpgradeFields) ValidateBasic() error { +func (muf UpgradeFields) ValidateBasic() error { if !collections.Contains(muf.Ordering, []Order{ORDERED, UNORDERED}) { return errorsmod.Wrap(ErrInvalidChannelOrdering, muf.Ordering.String()) } @@ -30,7 +32,7 @@ func (muf ModifiableUpgradeFields) ValidateBasic() error { return errorsmod.Wrap(ErrTooManyConnectionHops, "current IBC version only supports one connection hop") } - if muf.Version == "" { + if strings.TrimSpace(muf.Version) == "" { return errorsmod.Wrap(ErrInvalidUpgrade, "proposed upgrade version cannot be empty") } diff --git a/modules/core/04-channel/types/upgrade.pb.go b/modules/core/04-channel/types/upgrade.pb.go index df233e756ef..3cf676ce5c5 100644 --- a/modules/core/04-channel/types/upgrade.pb.go +++ b/modules/core/04-channel/types/upgrade.pb.go @@ -29,9 +29,9 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // end, the timeout for this upgrade attempt and the last packet sequence sent // to allow the counterparty to block sends after the upgrade has started. type Upgrade struct { - UpgradeFields ModifiableUpgradeFields `protobuf:"bytes,1,opt,name=upgrade_fields,json=upgradeFields,proto3" json:"upgrade_fields"` - Timeout UpgradeTimeout `protobuf:"bytes,2,opt,name=timeout,proto3" json:"timeout"` - LastPacketSent uint64 `protobuf:"varint,3,opt,name=last_packet_sent,json=lastPacketSent,proto3" json:"last_packet_sent,omitempty"` + UpgradeFields UpgradeFields `protobuf:"bytes,1,opt,name=upgrade_fields,json=upgradeFields,proto3" json:"upgrade_fields"` + Timeout UpgradeTimeout `protobuf:"bytes,2,opt,name=timeout,proto3" json:"timeout"` + LastPacketSent uint64 `protobuf:"varint,3,opt,name=last_packet_sent,json=lastPacketSent,proto3" json:"last_packet_sent,omitempty"` } func (m *Upgrade) Reset() { *m = Upgrade{} } @@ -67,11 +67,11 @@ func (m *Upgrade) XXX_DiscardUnknown() { var xxx_messageInfo_Upgrade proto.InternalMessageInfo -func (m *Upgrade) GetUpgradeFields() ModifiableUpgradeFields { +func (m *Upgrade) GetUpgradeFields() UpgradeFields { if m != nil { return m.UpgradeFields } - return ModifiableUpgradeFields{} + return UpgradeFields{} } func (m *Upgrade) GetTimeout() UpgradeTimeout { @@ -90,24 +90,24 @@ func (m *Upgrade) GetLastPacketSent() uint64 { // ModifiableUpgradeFields are the fields in a channel end which may be changed // during a channel upgrade. -type ModifiableUpgradeFields struct { +type UpgradeFields struct { Ordering Order `protobuf:"varint,1,opt,name=ordering,proto3,enum=ibc.core.channel.v1.Order" json:"ordering,omitempty"` ConnectionHops []string `protobuf:"bytes,2,rep,name=connection_hops,json=connectionHops,proto3" json:"connection_hops,omitempty"` Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` } -func (m *ModifiableUpgradeFields) Reset() { *m = ModifiableUpgradeFields{} } -func (m *ModifiableUpgradeFields) String() string { return proto.CompactTextString(m) } -func (*ModifiableUpgradeFields) ProtoMessage() {} -func (*ModifiableUpgradeFields) Descriptor() ([]byte, []int) { +func (m *UpgradeFields) Reset() { *m = UpgradeFields{} } +func (m *UpgradeFields) String() string { return proto.CompactTextString(m) } +func (*UpgradeFields) ProtoMessage() {} +func (*UpgradeFields) Descriptor() ([]byte, []int) { return fileDescriptor_fb1cef68588848b2, []int{1} } -func (m *ModifiableUpgradeFields) XXX_Unmarshal(b []byte) error { +func (m *UpgradeFields) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *ModifiableUpgradeFields) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *UpgradeFields) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_ModifiableUpgradeFields.Marshal(b, m, deterministic) + return xxx_messageInfo_UpgradeFields.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -117,33 +117,33 @@ func (m *ModifiableUpgradeFields) XXX_Marshal(b []byte, deterministic bool) ([]b return b[:n], nil } } -func (m *ModifiableUpgradeFields) XXX_Merge(src proto.Message) { - xxx_messageInfo_ModifiableUpgradeFields.Merge(m, src) +func (m *UpgradeFields) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpgradeFields.Merge(m, src) } -func (m *ModifiableUpgradeFields) XXX_Size() int { +func (m *UpgradeFields) XXX_Size() int { return m.Size() } -func (m *ModifiableUpgradeFields) XXX_DiscardUnknown() { - xxx_messageInfo_ModifiableUpgradeFields.DiscardUnknown(m) +func (m *UpgradeFields) XXX_DiscardUnknown() { + xxx_messageInfo_UpgradeFields.DiscardUnknown(m) } -var xxx_messageInfo_ModifiableUpgradeFields proto.InternalMessageInfo +var xxx_messageInfo_UpgradeFields proto.InternalMessageInfo -func (m *ModifiableUpgradeFields) GetOrdering() Order { +func (m *UpgradeFields) GetOrdering() Order { if m != nil { return m.Ordering } return NONE } -func (m *ModifiableUpgradeFields) GetConnectionHops() []string { +func (m *UpgradeFields) GetConnectionHops() []string { if m != nil { return m.ConnectionHops } return nil } -func (m *ModifiableUpgradeFields) GetVersion() string { +func (m *UpgradeFields) GetVersion() string { if m != nil { return m.Version } @@ -265,7 +265,7 @@ func (m *ErrorReceipt) GetError() string { func init() { proto.RegisterType((*Upgrade)(nil), "ibc.core.channel.v1.Upgrade") - proto.RegisterType((*ModifiableUpgradeFields)(nil), "ibc.core.channel.v1.ModifiableUpgradeFields") + proto.RegisterType((*UpgradeFields)(nil), "ibc.core.channel.v1.UpgradeFields") proto.RegisterType((*UpgradeTimeout)(nil), "ibc.core.channel.v1.UpgradeTimeout") proto.RegisterType((*ErrorReceipt)(nil), "ibc.core.channel.v1.ErrorReceipt") } @@ -273,37 +273,37 @@ func init() { func init() { proto.RegisterFile("ibc/core/channel/v1/upgrade.proto", fileDescriptor_fb1cef68588848b2) } var fileDescriptor_fb1cef68588848b2 = []byte{ - // 479 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x31, 0x6f, 0xd4, 0x30, - 0x14, 0xc7, 0x2f, 0xed, 0xc1, 0xf5, 0x0c, 0x84, 0x62, 0x2a, 0x11, 0xdd, 0x90, 0x1e, 0xc7, 0xc0, - 0x49, 0xd0, 0x84, 0x2b, 0x08, 0xc4, 0x86, 0x8a, 0x80, 0x2e, 0x08, 0x94, 0x96, 0x01, 0x96, 0x28, - 0x71, 0x5e, 0x13, 0x8b, 0xc4, 0x2f, 0xc4, 0x4e, 0x24, 0xbe, 0x00, 0x33, 0x03, 0x1f, 0xaa, 0x63, - 0x25, 0x16, 0x26, 0x84, 0xee, 0xbe, 0x08, 0x8a, 0xe3, 0xb4, 0x45, 0x3a, 0xa6, 0xd8, 0x7f, 0xff, - 0xde, 0xdf, 0x7f, 0xbf, 0x3c, 0x72, 0x97, 0xc7, 0xcc, 0x67, 0x58, 0x81, 0xcf, 0xb2, 0x48, 0x08, - 0xc8, 0xfd, 0x66, 0xe1, 0xd7, 0x65, 0x5a, 0x45, 0x09, 0x78, 0x65, 0x85, 0x0a, 0xe9, 0x6d, 0x1e, - 0x33, 0xaf, 0x45, 0x3c, 0x83, 0x78, 0xcd, 0x62, 0xb2, 0x93, 0x62, 0x8a, 0xfa, 0xdc, 0x6f, 0x57, - 0x1d, 0x3a, 0xd9, 0xbd, 0x70, 0xcb, 0x39, 0x08, 0xd5, 0x9a, 0x75, 0x2b, 0x03, 0xac, 0xbd, 0xae, - 0xb7, 0xd5, 0xc8, 0xec, 0xa7, 0x45, 0x46, 0x1f, 0xba, 0x00, 0xf4, 0x23, 0xb1, 0x4d, 0x96, 0xf0, - 0x84, 0x43, 0x9e, 0x48, 0xc7, 0x9a, 0x5a, 0xf3, 0x6b, 0xfb, 0x0f, 0xbd, 0x35, 0x99, 0xbc, 0xb7, - 0x98, 0xf0, 0x13, 0x1e, 0xc5, 0x39, 0x98, 0xfa, 0xd7, 0xba, 0xe6, 0x60, 0x78, 0xfa, 0x7b, 0x77, - 0x10, 0xdc, 0xa8, 0x2f, 0x8b, 0xf4, 0x25, 0x19, 0x29, 0x5e, 0x00, 0xd6, 0xca, 0xd9, 0xd0, 0x9e, - 0xf7, 0xd6, 0x7a, 0x1a, 0xa7, 0xe3, 0x0e, 0x35, 0x56, 0x7d, 0x25, 0x9d, 0x93, 0xed, 0x3c, 0x92, - 0x2a, 0x2c, 0x23, 0xf6, 0x19, 0x54, 0x28, 0x41, 0x28, 0x67, 0x73, 0x6a, 0xcd, 0x87, 0x81, 0xdd, - 0xea, 0xef, 0xb5, 0x7c, 0x04, 0x42, 0xcd, 0x7e, 0x58, 0xe4, 0xce, 0x7f, 0xf2, 0xd1, 0xa7, 0x64, - 0x0b, 0xab, 0x04, 0x2a, 0x2e, 0x52, 0xfd, 0x3e, 0x7b, 0x7f, 0xb2, 0x36, 0xcb, 0xbb, 0x16, 0x0a, - 0xce, 0x59, 0x7a, 0x9f, 0xdc, 0x64, 0x28, 0x04, 0x30, 0xc5, 0x51, 0x84, 0x19, 0x96, 0xd2, 0xd9, - 0x98, 0x6e, 0xce, 0xc7, 0x81, 0x7d, 0x21, 0x1f, 0x62, 0x29, 0xa9, 0x43, 0x46, 0x0d, 0x54, 0x92, - 0xa3, 0xd0, 0xe9, 0xc6, 0x41, 0xbf, 0x9d, 0x7d, 0xb3, 0x88, 0xfd, 0xef, 0x13, 0xe9, 0x1b, 0x62, - 0x9b, 0xe7, 0x85, 0x19, 0xf0, 0x34, 0x53, 0xa6, 0xe7, 0x97, 0x33, 0x75, 0xbf, 0xb4, 0x59, 0x78, - 0x87, 0x9a, 0xe8, 0x3b, 0x6c, 0xea, 0x3a, 0x91, 0x3e, 0x20, 0xb7, 0x7a, 0xa3, 0xf6, 0x2b, 0x55, - 0x54, 0x94, 0xba, 0xd7, 0xc3, 0x60, 0xdb, 0x1c, 0x1c, 0xf7, 0xfa, 0xec, 0x05, 0xb9, 0xfe, 0xaa, - 0xaa, 0xb0, 0x0a, 0x80, 0x01, 0x2f, 0x15, 0x9d, 0x90, 0x2d, 0x09, 0x5f, 0x6a, 0x10, 0x0c, 0xf4, - 0xfd, 0xc3, 0xe0, 0x7c, 0x4f, 0x77, 0xc8, 0x15, 0x68, 0x59, 0x6d, 0x36, 0x0e, 0xba, 0xcd, 0xc1, - 0xd1, 0xe9, 0xd2, 0xb5, 0xce, 0x96, 0xae, 0xf5, 0x67, 0xe9, 0x5a, 0xdf, 0x57, 0xee, 0xe0, 0x6c, - 0xe5, 0x0e, 0x7e, 0xad, 0xdc, 0xc1, 0xa7, 0xe7, 0x29, 0x57, 0x59, 0x1d, 0x7b, 0x0c, 0x0b, 0x9f, - 0xa1, 0x2c, 0x50, 0xfa, 0x3c, 0x66, 0x7b, 0x29, 0xfa, 0xcd, 0x33, 0xbf, 0xc0, 0xa4, 0xce, 0x41, - 0x76, 0x43, 0xf9, 0xe8, 0xc9, 0x5e, 0x3f, 0x97, 0xea, 0x6b, 0x09, 0x32, 0xbe, 0xaa, 0x67, 0xf2, - 0xf1, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa8, 0x15, 0x49, 0xab, 0x27, 0x03, 0x00, 0x00, + // 467 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0x31, 0x6f, 0xd4, 0x30, + 0x14, 0xc7, 0x2f, 0xed, 0xc1, 0xf5, 0x0c, 0x0d, 0xc5, 0x74, 0x88, 0x6e, 0x48, 0x8f, 0x30, 0x70, + 0x12, 0x6a, 0xc2, 0x15, 0x04, 0x62, 0x43, 0x45, 0x40, 0xb7, 0xa2, 0xb4, 0x2c, 0x2c, 0x51, 0xe2, + 0x7b, 0x24, 0x16, 0x89, 0x5f, 0x88, 0x9d, 0x48, 0x7c, 0x01, 0x06, 0x26, 0x3e, 0x56, 0xd9, 0x3a, + 0x32, 0x21, 0x74, 0xf7, 0x45, 0x50, 0x1c, 0xa7, 0xed, 0x49, 0x27, 0xa6, 0xd8, 0x7f, 0xff, 0xfc, + 0xf7, 0xff, 0xbd, 0x3c, 0xf2, 0x90, 0x27, 0x2c, 0x60, 0x58, 0x41, 0xc0, 0xb2, 0x58, 0x08, 0xc8, + 0x83, 0x66, 0x1e, 0xd4, 0x65, 0x5a, 0xc5, 0x0b, 0xf0, 0xcb, 0x0a, 0x15, 0xd2, 0x07, 0x3c, 0x61, + 0x7e, 0x8b, 0xf8, 0x06, 0xf1, 0x9b, 0xf9, 0x64, 0x3f, 0xc5, 0x14, 0xf5, 0x79, 0xd0, 0xae, 0x3a, + 0x74, 0x72, 0x70, 0xed, 0x96, 0x73, 0x10, 0xaa, 0x35, 0xeb, 0x56, 0x06, 0xd8, 0xf8, 0x5c, 0x6f, + 0xab, 0x11, 0xef, 0x97, 0x45, 0x46, 0x1f, 0xbb, 0x00, 0xf4, 0x94, 0xd8, 0x26, 0x4b, 0xf4, 0x99, + 0x43, 0xbe, 0x90, 0x8e, 0x35, 0xb5, 0x66, 0x77, 0x8e, 0x3c, 0x7f, 0x43, 0x26, 0xdf, 0xdc, 0x7a, + 0xa7, 0xc9, 0xe3, 0xe1, 0xc5, 0x9f, 0x83, 0x41, 0xb8, 0x5b, 0xdf, 0x14, 0xe9, 0x1b, 0x32, 0x52, + 0xbc, 0x00, 0xac, 0x95, 0xb3, 0xa5, 0x9d, 0x1e, 0xfd, 0xcf, 0xe9, 0xbc, 0x43, 0x8d, 0x55, 0x7f, + 0x93, 0xce, 0xc8, 0x5e, 0x1e, 0x4b, 0x15, 0x95, 0x31, 0xfb, 0x02, 0x2a, 0x92, 0x20, 0x94, 0xb3, + 0x3d, 0xb5, 0x66, 0xc3, 0xd0, 0x6e, 0xf5, 0x0f, 0x5a, 0x3e, 0x03, 0xa1, 0xbc, 0x1f, 0x16, 0xd9, + 0x5d, 0x4b, 0x45, 0x5f, 0x90, 0x1d, 0xac, 0x16, 0x50, 0x71, 0x91, 0xea, 0x5a, 0xec, 0xa3, 0xc9, + 0xc6, 0x04, 0xa7, 0x2d, 0x14, 0x5e, 0xb1, 0xf4, 0x31, 0xb9, 0xc7, 0x50, 0x08, 0x60, 0x8a, 0xa3, + 0x88, 0x32, 0x2c, 0xa5, 0xb3, 0x35, 0xdd, 0x9e, 0x8d, 0x43, 0xfb, 0x5a, 0x3e, 0xc1, 0x52, 0x52, + 0x87, 0x8c, 0x1a, 0xa8, 0x24, 0x47, 0xa1, 0x33, 0x8d, 0xc3, 0x7e, 0xeb, 0x7d, 0xb7, 0x88, 0xbd, + 0x5e, 0x18, 0x7d, 0x4f, 0x6c, 0x53, 0x54, 0x94, 0x01, 0x4f, 0x33, 0x65, 0xfa, 0x7b, 0x33, 0x53, + 0xf7, 0xfb, 0x9a, 0xb9, 0x7f, 0xa2, 0x89, 0xbe, 0xaf, 0xe6, 0x5e, 0x27, 0xd2, 0x27, 0xe4, 0x7e, + 0x6f, 0xd4, 0x7e, 0xa5, 0x8a, 0x8b, 0x52, 0x77, 0x78, 0x18, 0xee, 0x99, 0x83, 0xf3, 0x5e, 0xf7, + 0x5e, 0x93, 0xbb, 0x6f, 0xab, 0x0a, 0xab, 0x10, 0x18, 0xf0, 0x52, 0xd1, 0x09, 0xd9, 0x91, 0xf0, + 0xb5, 0x06, 0xc1, 0x40, 0xbf, 0x3f, 0x0c, 0xaf, 0xf6, 0x74, 0x9f, 0xdc, 0x82, 0x96, 0xd5, 0x66, + 0xe3, 0xb0, 0xdb, 0x1c, 0x9f, 0x5d, 0x2c, 0x5d, 0xeb, 0x72, 0xe9, 0x5a, 0x7f, 0x97, 0xae, 0xf5, + 0x73, 0xe5, 0x0e, 0x2e, 0x57, 0xee, 0xe0, 0xf7, 0xca, 0x1d, 0x7c, 0x7a, 0x95, 0x72, 0x95, 0xd5, + 0x89, 0xcf, 0xb0, 0x08, 0x18, 0xca, 0x02, 0x65, 0xc0, 0x13, 0x76, 0x98, 0x62, 0xd0, 0xbc, 0x0c, + 0x0a, 0x5c, 0xd4, 0x39, 0xc8, 0x6e, 0x00, 0x9f, 0x3e, 0x3f, 0xec, 0x67, 0x50, 0x7d, 0x2b, 0x41, + 0x26, 0xb7, 0xf5, 0xfc, 0x3d, 0xfb, 0x17, 0x00, 0x00, 0xff, 0xff, 0xa6, 0xd7, 0x1c, 0xc9, 0x13, + 0x03, 0x00, 0x00, } func (m *Upgrade) Marshal() (dAtA []byte, err error) { @@ -354,7 +354,7 @@ func (m *Upgrade) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *ModifiableUpgradeFields) Marshal() (dAtA []byte, err error) { +func (m *UpgradeFields) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -364,12 +364,12 @@ func (m *ModifiableUpgradeFields) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *ModifiableUpgradeFields) MarshalTo(dAtA []byte) (int, error) { +func (m *UpgradeFields) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *ModifiableUpgradeFields) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *UpgradeFields) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -498,7 +498,7 @@ func (m *Upgrade) Size() (n int) { return n } -func (m *ModifiableUpgradeFields) Size() (n int) { +func (m *UpgradeFields) Size() (n int) { if m == nil { return 0 } @@ -691,7 +691,7 @@ func (m *Upgrade) Unmarshal(dAtA []byte) error { } return nil } -func (m *ModifiableUpgradeFields) Unmarshal(dAtA []byte) error { +func (m *UpgradeFields) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -714,10 +714,10 @@ func (m *ModifiableUpgradeFields) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ModifiableUpgradeFields: wiretype end group for non-group") + return fmt.Errorf("proto: UpgradeFields: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ModifiableUpgradeFields: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: UpgradeFields: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: diff --git a/proto/ibc/core/channel/v1/upgrade.proto b/proto/ibc/core/channel/v1/upgrade.proto index 03e63a0d2c1..a3d0b86effc 100644 --- a/proto/ibc/core/channel/v1/upgrade.proto +++ b/proto/ibc/core/channel/v1/upgrade.proto @@ -13,14 +13,14 @@ import "ibc/core/channel/v1/channel.proto"; // end, the timeout for this upgrade attempt and the last packet sequence sent // to allow the counterparty to block sends after the upgrade has started. message Upgrade { - ModifiableUpgradeFields upgrade_fields = 1 [(gogoproto.nullable) = false]; - UpgradeTimeout timeout = 2 [(gogoproto.nullable) = false]; - uint64 last_packet_sent = 3; + UpgradeFields upgrade_fields = 1 [(gogoproto.nullable) = false]; + UpgradeTimeout timeout = 2 [(gogoproto.nullable) = false]; + uint64 last_packet_sent = 3; } // ModifiableUpgradeFields are the fields in a channel end which may be changed // during a channel upgrade. -message ModifiableUpgradeFields { +message UpgradeFields { Order ordering = 1; repeated string connection_hops = 2; string version = 3; From d66fe9de8a34f6c931751046a44522ab750e02a2 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Mon, 17 Apr 2023 09:34:48 +0100 Subject: [PATCH 10/18] addressing PR feedback --- .../core/03-connection/keeper/keeper_test.go | 62 ----------- modules/core/04-channel/keeper/keeper.go | 15 ++- modules/core/04-channel/keeper/keeper_test.go | 6 +- .../core/04-channel/types/expected_keepers.go | 2 - modules/core/04-channel/types/upgrade.go | 14 +-- modules/core/04-channel/types/upgrade.pb.go | 101 +++++++++--------- proto/ibc/core/channel/v1/upgrade.proto | 10 +- 7 files changed, 79 insertions(+), 131 deletions(-) diff --git a/modules/core/03-connection/keeper/keeper_test.go b/modules/core/03-connection/keeper/keeper_test.go index 2d0b42dbab9..dbae331d690 100644 --- a/modules/core/03-connection/keeper/keeper_test.go +++ b/modules/core/03-connection/keeper/keeper_test.go @@ -8,7 +8,6 @@ import ( "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v7/modules/core/24-host" "github.com/cosmos/ibc-go/v7/modules/core/exported" ibctesting "github.com/cosmos/ibc-go/v7/testing" ) @@ -177,64 +176,3 @@ func (suite *KeeperTestSuite) TestLocalhostConnectionEndCreation() { expectedCounterParty := types.NewCounterparty(exported.LocalhostClientID, exported.LocalhostConnectionID, commitmenttypes.NewMerklePrefix(connectionKeeper.GetCommitmentPrefix().Bytes())) suite.Require().Equal(expectedCounterParty, connectionEnd.Counterparty) } - -func (suite *KeeperTestSuite) TestCheckIsOpen() { - var path *ibctesting.Path - - cases := []struct { - msg string - malleate func() - expPass bool - }{ - { - msg: "success", - malleate: func() {}, - expPass: true, - }, - { - msg: "uninitialized connection", - malleate: func() { - connection := path.EndpointA.GetConnection() - connection.State = types.UNINITIALIZED - path.EndpointA.SetConnection(connection) - }, - expPass: false, - }, - { - msg: "init connection", - malleate: func() { - connection := path.EndpointA.GetConnection() - connection.State = types.INIT - path.EndpointA.SetConnection(connection) - }, - expPass: false, - }, - { - msg: "no connection", - malleate: func() { - storeKey := suite.chainA.GetSimApp().GetKey(exported.StoreKey) - kvStore := suite.chainA.GetContext().KVStore(storeKey) - kvStore.Delete(host.ConnectionKey(ibctesting.FirstConnectionID)) - }, - expPass: false, - }, - } - - for _, tc := range cases { - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - suite.SetupTest() // reset - path = ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.Setup(path) - - tc.malleate() - - connectionKeeper := suite.chainA.GetSimApp().IBCKeeper.ConnectionKeeper - - if tc.expPass { - suite.Require().NoError(connectionKeeper.CheckIsOpen(suite.chainA.GetContext(), ibctesting.FirstConnectionID)) - } else { - suite.Require().Error(connectionKeeper.CheckIsOpen(suite.chainA.GetContext(), ibctesting.FirstConnectionID)) - } - }) - } -} diff --git a/modules/core/04-channel/keeper/keeper.go b/modules/core/04-channel/keeper/keeper.go index b8f30f89c59..e4112b4737d 100644 --- a/modules/core/04-channel/keeper/keeper.go +++ b/modules/core/04-channel/keeper/keeper.go @@ -593,7 +593,20 @@ func (k Keeper) ValidateUpgradeFields(ctx sdk.Context, proposedUpgrade types.Upg return errorsmod.Wrap(types.ErrInvalidChannelOrdering, "channel ordering must be a subset of the new ordering") } - return k.connectionKeeper.CheckIsOpen(ctx, proposedUpgrade.ConnectionHops[0]) + connectionID := proposedUpgrade.ConnectionHops[0] + connection, err := k.GetConnection(ctx, connectionID) + if err != nil { + return errorsmod.Wrapf(connectiontypes.ErrConnectionNotFound, "failed to retrieve connection: %s", connectionID) + } + + if connection.GetState() != int32(connectiontypes.OPEN) { + return errorsmod.Wrapf( + connectiontypes.ErrInvalidConnectionState, + "connection state is not OPEN (got %s)", connectiontypes.State(connection.GetState()).String(), + ) + } + + return nil } // common functionality for IteratePacketCommitment and IteratePacketAcknowledgement diff --git a/modules/core/04-channel/keeper/keeper_test.go b/modules/core/04-channel/keeper/keeper_test.go index 8af0c1c2c48..a92f2b2b6ad 100644 --- a/modules/core/04-channel/keeper/keeper_test.go +++ b/modules/core/04-channel/keeper/keeper_test.go @@ -596,12 +596,12 @@ func (suite *KeeperTestSuite) TestValidateProposedUpgradeFields() { expPass: false, }, { - name: "no modified fields in proposed upgrade", + name: "fails with unmodified fields", malleate: func() {}, expPass: false, }, { - name: "attempt upgrade when connection is not set", + name: "fails when connection is not set", malleate: func() { storeKey := suite.chainA.GetSimApp().GetKey(exported.StoreKey) kvStore := suite.chainA.GetContext().KVStore(storeKey) @@ -610,7 +610,7 @@ func (suite *KeeperTestSuite) TestValidateProposedUpgradeFields() { expPass: false, }, { - name: "attempt upgrade when connection is not open", + name: "fails when connection is not open", malleate: func() { connection := path.EndpointA.GetConnection() connection.State = connectiontypes.UNINITIALIZED diff --git a/modules/core/04-channel/types/expected_keepers.go b/modules/core/04-channel/types/expected_keepers.go index b7552470134..3c0b0b0198d 100644 --- a/modules/core/04-channel/types/expected_keepers.go +++ b/modules/core/04-channel/types/expected_keepers.go @@ -18,8 +18,6 @@ type ClientKeeper interface { // ConnectionKeeper expected account IBC connection keeper type ConnectionKeeper interface { - CheckIsOpen(ctx sdk.Context, connectionID string) error - HasConnection(ctx sdk.Context, connectionID string) bool GetConnection(ctx sdk.Context, connectionID string) (connectiontypes.ConnectionEnd, bool) GetTimestampAtHeight( ctx sdk.Context, diff --git a/modules/core/04-channel/types/upgrade.go b/modules/core/04-channel/types/upgrade.go index 5673543f79f..4961c985f2e 100644 --- a/modules/core/04-channel/types/upgrade.go +++ b/modules/core/04-channel/types/upgrade.go @@ -10,7 +10,7 @@ import ( // ValidateBasic performs a basic validation of the upgrade fields func (u Upgrade) ValidateBasic() error { - if err := u.UpgradeFields.ValidateBasic(); err != nil { + if err := u.Fields.ValidateBasic(); err != nil { return errorsmod.Wrap(err, "proposed upgrade fields are invalid") } @@ -23,17 +23,17 @@ func (u Upgrade) ValidateBasic() error { } // ValidateBasic performs a basic validation of the proposed upgrade fields -func (muf UpgradeFields) ValidateBasic() error { - if !collections.Contains(muf.Ordering, []Order{ORDERED, UNORDERED}) { - return errorsmod.Wrap(ErrInvalidChannelOrdering, muf.Ordering.String()) +func (uf UpgradeFields) ValidateBasic() error { + if !collections.Contains(uf.Ordering, []Order{ORDERED, UNORDERED}) { + return errorsmod.Wrap(ErrInvalidChannelOrdering, uf.Ordering.String()) } - if len(muf.ConnectionHops) != 1 { + if len(uf.ConnectionHops) != 1 { return errorsmod.Wrap(ErrTooManyConnectionHops, "current IBC version only supports one connection hop") } - if strings.TrimSpace(muf.Version) == "" { - return errorsmod.Wrap(ErrInvalidUpgrade, "proposed upgrade version cannot be empty") + if strings.TrimSpace(uf.Version) == "" { + return errorsmod.Wrap(ErrInvalidChannelVersion, "version cannot be empty") } return nil diff --git a/modules/core/04-channel/types/upgrade.pb.go b/modules/core/04-channel/types/upgrade.pb.go index 3cf676ce5c5..42329473de1 100644 --- a/modules/core/04-channel/types/upgrade.pb.go +++ b/modules/core/04-channel/types/upgrade.pb.go @@ -26,12 +26,12 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // Upgrade is a verifiable type which contains the relevant information // for an attempted upgrade. It provides the proposed changes to the channel -// end, the timeout for this upgrade attempt and the last packet sequence sent +// end, the timeout for this upgrade attempt and the latest packet sequence sent // to allow the counterparty to block sends after the upgrade has started. type Upgrade struct { - UpgradeFields UpgradeFields `protobuf:"bytes,1,opt,name=upgrade_fields,json=upgradeFields,proto3" json:"upgrade_fields"` - Timeout UpgradeTimeout `protobuf:"bytes,2,opt,name=timeout,proto3" json:"timeout"` - LastPacketSent uint64 `protobuf:"varint,3,opt,name=last_packet_sent,json=lastPacketSent,proto3" json:"last_packet_sent,omitempty"` + Fields UpgradeFields `protobuf:"bytes,1,opt,name=fields,proto3" json:"fields"` + Timeout UpgradeTimeout `protobuf:"bytes,2,opt,name=timeout,proto3" json:"timeout"` + LatestSequenceSend uint64 `protobuf:"varint,3,opt,name=latest_sequence_send,json=latestSequenceSend,proto3" json:"latest_sequence_send,omitempty"` } func (m *Upgrade) Reset() { *m = Upgrade{} } @@ -67,9 +67,9 @@ func (m *Upgrade) XXX_DiscardUnknown() { var xxx_messageInfo_Upgrade proto.InternalMessageInfo -func (m *Upgrade) GetUpgradeFields() UpgradeFields { +func (m *Upgrade) GetFields() UpgradeFields { if m != nil { - return m.UpgradeFields + return m.Fields } return UpgradeFields{} } @@ -81,14 +81,14 @@ func (m *Upgrade) GetTimeout() UpgradeTimeout { return UpgradeTimeout{} } -func (m *Upgrade) GetLastPacketSent() uint64 { +func (m *Upgrade) GetLatestSequenceSend() uint64 { if m != nil { - return m.LastPacketSent + return m.LatestSequenceSend } return 0 } -// ModifiableUpgradeFields are the fields in a channel end which may be changed +// UpgradeFields are the fields in a channel end which may be changed // during a channel upgrade. type UpgradeFields struct { Ordering Order `protobuf:"varint,1,opt,name=ordering,proto3,enum=ibc.core.channel.v1.Order" json:"ordering,omitempty"` @@ -273,37 +273,36 @@ func init() { func init() { proto.RegisterFile("ibc/core/channel/v1/upgrade.proto", fileDescriptor_fb1cef68588848b2) } var fileDescriptor_fb1cef68588848b2 = []byte{ - // 467 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0x31, 0x6f, 0xd4, 0x30, - 0x14, 0xc7, 0x2f, 0xed, 0xc1, 0xf5, 0x0c, 0x0d, 0xc5, 0x74, 0x88, 0x6e, 0x48, 0x8f, 0x30, 0x70, - 0x12, 0x6a, 0xc2, 0x15, 0x04, 0x62, 0x43, 0x45, 0x40, 0xb7, 0xa2, 0xb4, 0x2c, 0x2c, 0x51, 0xe2, - 0x7b, 0x24, 0x16, 0x89, 0x5f, 0x88, 0x9d, 0x48, 0x7c, 0x01, 0x06, 0x26, 0x3e, 0x56, 0xd9, 0x3a, - 0x32, 0x21, 0x74, 0xf7, 0x45, 0x50, 0x1c, 0xa7, 0xed, 0x49, 0x27, 0xa6, 0xd8, 0x7f, 0xff, 0xfc, - 0xf7, 0xff, 0xbd, 0x3c, 0xf2, 0x90, 0x27, 0x2c, 0x60, 0x58, 0x41, 0xc0, 0xb2, 0x58, 0x08, 0xc8, - 0x83, 0x66, 0x1e, 0xd4, 0x65, 0x5a, 0xc5, 0x0b, 0xf0, 0xcb, 0x0a, 0x15, 0xd2, 0x07, 0x3c, 0x61, - 0x7e, 0x8b, 0xf8, 0x06, 0xf1, 0x9b, 0xf9, 0x64, 0x3f, 0xc5, 0x14, 0xf5, 0x79, 0xd0, 0xae, 0x3a, - 0x74, 0x72, 0x70, 0xed, 0x96, 0x73, 0x10, 0xaa, 0x35, 0xeb, 0x56, 0x06, 0xd8, 0xf8, 0x5c, 0x6f, - 0xab, 0x11, 0xef, 0x97, 0x45, 0x46, 0x1f, 0xbb, 0x00, 0xf4, 0x94, 0xd8, 0x26, 0x4b, 0xf4, 0x99, - 0x43, 0xbe, 0x90, 0x8e, 0x35, 0xb5, 0x66, 0x77, 0x8e, 0x3c, 0x7f, 0x43, 0x26, 0xdf, 0xdc, 0x7a, - 0xa7, 0xc9, 0xe3, 0xe1, 0xc5, 0x9f, 0x83, 0x41, 0xb8, 0x5b, 0xdf, 0x14, 0xe9, 0x1b, 0x32, 0x52, - 0xbc, 0x00, 0xac, 0x95, 0xb3, 0xa5, 0x9d, 0x1e, 0xfd, 0xcf, 0xe9, 0xbc, 0x43, 0x8d, 0x55, 0x7f, - 0x93, 0xce, 0xc8, 0x5e, 0x1e, 0x4b, 0x15, 0x95, 0x31, 0xfb, 0x02, 0x2a, 0x92, 0x20, 0x94, 0xb3, - 0x3d, 0xb5, 0x66, 0xc3, 0xd0, 0x6e, 0xf5, 0x0f, 0x5a, 0x3e, 0x03, 0xa1, 0xbc, 0x1f, 0x16, 0xd9, - 0x5d, 0x4b, 0x45, 0x5f, 0x90, 0x1d, 0xac, 0x16, 0x50, 0x71, 0x91, 0xea, 0x5a, 0xec, 0xa3, 0xc9, - 0xc6, 0x04, 0xa7, 0x2d, 0x14, 0x5e, 0xb1, 0xf4, 0x31, 0xb9, 0xc7, 0x50, 0x08, 0x60, 0x8a, 0xa3, - 0x88, 0x32, 0x2c, 0xa5, 0xb3, 0x35, 0xdd, 0x9e, 0x8d, 0x43, 0xfb, 0x5a, 0x3e, 0xc1, 0x52, 0x52, - 0x87, 0x8c, 0x1a, 0xa8, 0x24, 0x47, 0xa1, 0x33, 0x8d, 0xc3, 0x7e, 0xeb, 0x7d, 0xb7, 0x88, 0xbd, - 0x5e, 0x18, 0x7d, 0x4f, 0x6c, 0x53, 0x54, 0x94, 0x01, 0x4f, 0x33, 0x65, 0xfa, 0x7b, 0x33, 0x53, - 0xf7, 0xfb, 0x9a, 0xb9, 0x7f, 0xa2, 0x89, 0xbe, 0xaf, 0xe6, 0x5e, 0x27, 0xd2, 0x27, 0xe4, 0x7e, - 0x6f, 0xd4, 0x7e, 0xa5, 0x8a, 0x8b, 0x52, 0x77, 0x78, 0x18, 0xee, 0x99, 0x83, 0xf3, 0x5e, 0xf7, - 0x5e, 0x93, 0xbb, 0x6f, 0xab, 0x0a, 0xab, 0x10, 0x18, 0xf0, 0x52, 0xd1, 0x09, 0xd9, 0x91, 0xf0, - 0xb5, 0x06, 0xc1, 0x40, 0xbf, 0x3f, 0x0c, 0xaf, 0xf6, 0x74, 0x9f, 0xdc, 0x82, 0x96, 0xd5, 0x66, - 0xe3, 0xb0, 0xdb, 0x1c, 0x9f, 0x5d, 0x2c, 0x5d, 0xeb, 0x72, 0xe9, 0x5a, 0x7f, 0x97, 0xae, 0xf5, - 0x73, 0xe5, 0x0e, 0x2e, 0x57, 0xee, 0xe0, 0xf7, 0xca, 0x1d, 0x7c, 0x7a, 0x95, 0x72, 0x95, 0xd5, - 0x89, 0xcf, 0xb0, 0x08, 0x18, 0xca, 0x02, 0x65, 0xc0, 0x13, 0x76, 0x98, 0x62, 0xd0, 0xbc, 0x0c, - 0x0a, 0x5c, 0xd4, 0x39, 0xc8, 0x6e, 0x00, 0x9f, 0x3e, 0x3f, 0xec, 0x67, 0x50, 0x7d, 0x2b, 0x41, - 0x26, 0xb7, 0xf5, 0xfc, 0x3d, 0xfb, 0x17, 0x00, 0x00, 0xff, 0xff, 0xa6, 0xd7, 0x1c, 0xc9, 0x13, - 0x03, 0x00, 0x00, + // 463 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0x31, 0x6f, 0xd3, 0x40, + 0x14, 0xc7, 0xe3, 0x36, 0x34, 0xcd, 0x41, 0x03, 0x1c, 0x19, 0xac, 0x0c, 0x6e, 0x30, 0x03, 0x91, + 0x50, 0xed, 0xa6, 0x20, 0x10, 0x5b, 0x55, 0x04, 0x74, 0x43, 0x72, 0xca, 0xc2, 0x12, 0xc5, 0xe7, + 0x87, 0x7d, 0x92, 0x7d, 0xcf, 0xf8, 0xce, 0x96, 0xf8, 0x02, 0x0c, 0x4c, 0x7c, 0x26, 0xa6, 0x8e, + 0x1d, 0x99, 0x10, 0x4a, 0xbe, 0x08, 0xf2, 0xdd, 0xb9, 0xa5, 0x52, 0xc4, 0xe4, 0xbb, 0xf7, 0x7e, + 0xef, 0x7f, 0xff, 0xf7, 0xfc, 0xc8, 0x63, 0x1e, 0xb3, 0x90, 0x61, 0x05, 0x21, 0xcb, 0x56, 0x42, + 0x40, 0x1e, 0x36, 0xf3, 0xb0, 0x2e, 0xd3, 0x6a, 0x95, 0x40, 0x50, 0x56, 0xa8, 0x90, 0x3e, 0xe2, + 0x31, 0x0b, 0x5a, 0x24, 0xb0, 0x48, 0xd0, 0xcc, 0x27, 0xe3, 0x14, 0x53, 0xd4, 0xf9, 0xb0, 0x3d, + 0x19, 0x74, 0x72, 0x78, 0xa3, 0x96, 0x73, 0x10, 0xaa, 0x15, 0x33, 0x27, 0x0b, 0x6c, 0x7d, 0xae, + 0x93, 0xd5, 0x88, 0xff, 0xd3, 0x21, 0x83, 0x8f, 0xc6, 0x00, 0x3d, 0x25, 0x7b, 0x9f, 0x39, 0xe4, + 0x89, 0x74, 0x9d, 0xa9, 0x33, 0xbb, 0x7b, 0xe2, 0x07, 0x5b, 0xbc, 0x04, 0x96, 0x7e, 0xa7, 0xc9, + 0xb3, 0xfe, 0xe5, 0xef, 0xc3, 0x5e, 0x64, 0xeb, 0xe8, 0x1b, 0x32, 0x50, 0xbc, 0x00, 0xac, 0x95, + 0xbb, 0xa3, 0x25, 0x9e, 0xfc, 0x4f, 0xe2, 0xc2, 0xa0, 0x56, 0xa3, 0xab, 0xa4, 0xc7, 0x64, 0x9c, + 0xaf, 0x14, 0x48, 0xb5, 0x94, 0xf0, 0xa5, 0x06, 0xc1, 0x60, 0x29, 0x41, 0x24, 0xee, 0xee, 0xd4, + 0x99, 0xf5, 0x23, 0x6a, 0x72, 0x0b, 0x9b, 0x5a, 0x80, 0x48, 0xfc, 0xef, 0x0e, 0x39, 0xb8, 0x65, + 0x8b, 0xbe, 0x24, 0xfb, 0x58, 0x25, 0x50, 0x71, 0x91, 0xea, 0x66, 0x46, 0x27, 0x93, 0xad, 0x4e, + 0x3e, 0xb4, 0x50, 0x74, 0xcd, 0xd2, 0xa7, 0xe4, 0x3e, 0x43, 0x21, 0x80, 0x29, 0x8e, 0x62, 0x99, + 0x61, 0x29, 0xdd, 0x9d, 0xe9, 0xee, 0x6c, 0x18, 0x8d, 0x6e, 0xc2, 0xe7, 0x58, 0x4a, 0xea, 0x92, + 0x41, 0x03, 0x95, 0xe4, 0x28, 0xb4, 0xaf, 0x61, 0xd4, 0x5d, 0xfd, 0x6f, 0x0e, 0x19, 0xdd, 0x6e, + 0x90, 0xbe, 0x27, 0x23, 0xdb, 0xdc, 0x32, 0x03, 0x9e, 0x66, 0xca, 0x0e, 0xf8, 0x5f, 0x4f, 0xe6, + 0xbf, 0x35, 0xf3, 0xe0, 0x5c, 0x13, 0x76, 0x28, 0x07, 0xb6, 0xce, 0x04, 0xe9, 0x33, 0xf2, 0xb0, + 0x13, 0x6a, 0xbf, 0x52, 0xad, 0x8a, 0x52, 0x4f, 0xba, 0x1f, 0x3d, 0xb0, 0x89, 0x8b, 0x2e, 0xee, + 0x9f, 0x92, 0x7b, 0x6f, 0xab, 0x0a, 0xab, 0x08, 0x18, 0xf0, 0x52, 0xd1, 0x09, 0xd9, 0xef, 0x06, + 0xaa, 0xdf, 0xef, 0x47, 0xd7, 0x77, 0x3a, 0x26, 0x77, 0xa0, 0x65, 0xb5, 0xd8, 0x30, 0x32, 0x97, + 0xb3, 0xc5, 0xe5, 0xda, 0x73, 0xae, 0xd6, 0x9e, 0xf3, 0x67, 0xed, 0x39, 0x3f, 0x36, 0x5e, 0xef, + 0x6a, 0xe3, 0xf5, 0x7e, 0x6d, 0xbc, 0xde, 0xa7, 0xd7, 0x29, 0x57, 0x59, 0x1d, 0x07, 0x0c, 0x8b, + 0x90, 0xa1, 0x2c, 0x50, 0x86, 0x3c, 0x66, 0x47, 0x29, 0x86, 0xcd, 0xab, 0xb0, 0xc0, 0xa4, 0xce, + 0x41, 0x9a, 0xcd, 0x3b, 0x7e, 0x71, 0xd4, 0x2d, 0x9f, 0xfa, 0x5a, 0x82, 0x8c, 0xf7, 0xf4, 0xe2, + 0x3d, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xdd, 0x00, 0xd9, 0xd4, 0x0c, 0x03, 0x00, 0x00, } func (m *Upgrade) Marshal() (dAtA []byte, err error) { @@ -326,8 +325,8 @@ func (m *Upgrade) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.LastPacketSent != 0 { - i = encodeVarintUpgrade(dAtA, i, uint64(m.LastPacketSent)) + if m.LatestSequenceSend != 0 { + i = encodeVarintUpgrade(dAtA, i, uint64(m.LatestSequenceSend)) i-- dAtA[i] = 0x18 } @@ -342,7 +341,7 @@ func (m *Upgrade) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x12 { - size, err := m.UpgradeFields.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.Fields.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -488,12 +487,12 @@ func (m *Upgrade) Size() (n int) { } var l int _ = l - l = m.UpgradeFields.Size() + l = m.Fields.Size() n += 1 + l + sovUpgrade(uint64(l)) l = m.Timeout.Size() n += 1 + l + sovUpgrade(uint64(l)) - if m.LastPacketSent != 0 { - n += 1 + sovUpgrade(uint64(m.LastPacketSent)) + if m.LatestSequenceSend != 0 { + n += 1 + sovUpgrade(uint64(m.LatestSequenceSend)) } return n } @@ -587,7 +586,7 @@ func (m *Upgrade) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field UpgradeFields", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Fields", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -614,7 +613,7 @@ func (m *Upgrade) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.UpgradeFields.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Fields.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -653,9 +652,9 @@ func (m *Upgrade) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 3: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field LastPacketSent", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field LatestSequenceSend", wireType) } - m.LastPacketSent = 0 + m.LatestSequenceSend = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowUpgrade @@ -665,7 +664,7 @@ func (m *Upgrade) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.LastPacketSent |= uint64(b&0x7F) << shift + m.LatestSequenceSend |= uint64(b&0x7F) << shift if b < 0x80 { break } diff --git a/proto/ibc/core/channel/v1/upgrade.proto b/proto/ibc/core/channel/v1/upgrade.proto index a3d0b86effc..b54cd644113 100644 --- a/proto/ibc/core/channel/v1/upgrade.proto +++ b/proto/ibc/core/channel/v1/upgrade.proto @@ -10,15 +10,15 @@ import "ibc/core/channel/v1/channel.proto"; // Upgrade is a verifiable type which contains the relevant information // for an attempted upgrade. It provides the proposed changes to the channel -// end, the timeout for this upgrade attempt and the last packet sequence sent +// end, the timeout for this upgrade attempt and the latest packet sequence sent // to allow the counterparty to block sends after the upgrade has started. message Upgrade { - UpgradeFields upgrade_fields = 1 [(gogoproto.nullable) = false]; - UpgradeTimeout timeout = 2 [(gogoproto.nullable) = false]; - uint64 last_packet_sent = 3; + UpgradeFields fields = 1 [(gogoproto.nullable) = false]; + UpgradeTimeout timeout = 2 [(gogoproto.nullable) = false]; + uint64 latest_sequence_send = 3; } -// ModifiableUpgradeFields are the fields in a channel end which may be changed +// UpgradeFields are the fields in a channel end which may be changed // during a channel upgrade. message UpgradeFields { Order ordering = 1; From 18dbffe111e68ea9ba331582a1c40f7a96d16ad3 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Mon, 17 Apr 2023 11:12:18 +0100 Subject: [PATCH 11/18] addressing PR feedback --- modules/core/04-channel/keeper/keeper.go | 16 +++++++++++----- modules/core/04-channel/keeper/keeper_test.go | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/core/04-channel/keeper/keeper.go b/modules/core/04-channel/keeper/keeper.go index e4112b4737d..b944b2f9a62 100644 --- a/modules/core/04-channel/keeper/keeper.go +++ b/modules/core/04-channel/keeper/keeper.go @@ -580,11 +580,8 @@ func (k Keeper) DeleteUpgradeTimeout(ctx sdk.Context, portID, channelID string) // - the proposed version is non-empty (checked in ModifiableUpgradeFields.ValidateBasic()) // - the proposed connection hops are not open func (k Keeper) ValidateUpgradeFields(ctx sdk.Context, proposedUpgrade types.UpgradeFields, existingChannel types.Channel) error { - currentFields := types.UpgradeFields{ - Ordering: existingChannel.Ordering, - ConnectionHops: existingChannel.ConnectionHops, - Version: existingChannel.Version, - } + currentFields := extractUpgradeFields(existingChannel) + if reflect.DeepEqual(proposedUpgrade, currentFields) { return errorsmod.Wrap(types.ErrChannelExists, "existing channel end is identical to proposed upgrade channel end") } @@ -609,6 +606,15 @@ func (k Keeper) ValidateUpgradeFields(ctx sdk.Context, proposedUpgrade types.Upg return nil } +// extractUpgradeFields returns the upgrade fields from the provided channel. +func extractUpgradeFields(channel types.Channel) types.UpgradeFields { + return types.UpgradeFields{ + Ordering: channel.Ordering, + ConnectionHops: channel.ConnectionHops, + Version: channel.Version, + } +} + // common functionality for IteratePacketCommitment and IteratePacketAcknowledgement func (k Keeper) iterateHashes(ctx sdk.Context, iterator db.Iterator, cb func(portID, channelID string, sequence uint64, hash []byte) bool) { defer sdk.LogDeferred(ctx.Logger(), func() error { return iterator.Close() }) diff --git a/modules/core/04-channel/keeper/keeper_test.go b/modules/core/04-channel/keeper/keeper_test.go index a92f2b2b6ad..ff6d59d4d0b 100644 --- a/modules/core/04-channel/keeper/keeper_test.go +++ b/modules/core/04-channel/keeper/keeper_test.go @@ -589,7 +589,7 @@ func (suite *KeeperTestSuite) TestValidateProposedUpgradeFields() { expPass: true, }, { - name: "modify ordering to invalid value", + name: "fails with stricter ordering", malleate: func() { proposedUpgrade.Ordering = types.ORDERED }, From a3da0225f4f08cef977de774c4eb82626a4d74f4 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Mon, 17 Apr 2023 12:34:10 +0100 Subject: [PATCH 12/18] bump interchain account demo to v0.5.1 --- e2e/go.mod | 2 +- e2e/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/e2e/go.mod b/e2e/go.mod index a2c7342119e..d996125af2a 100644 --- a/e2e/go.mod +++ b/e2e/go.mod @@ -7,7 +7,7 @@ require ( github.com/cosmos/cosmos-sdk v0.47.0 github.com/cosmos/gogoproto v1.4.6 github.com/cosmos/ibc-go/v7 v7.0.0 - github.com/cosmos/interchain-accounts v0.5.0 + github.com/cosmos/interchain-accounts v0.5.1 github.com/docker/docker v20.10.19+incompatible github.com/strangelove-ventures/interchaintest/v7 v7.0.0-20230322043324-cb6ba0947fff github.com/stretchr/testify v1.8.2 diff --git a/e2e/go.sum b/e2e/go.sum index 5bed49460d7..6ec731d548f 100644 --- a/e2e/go.sum +++ b/e2e/go.sum @@ -347,8 +347,8 @@ github.com/cosmos/iavl v0.20.0 h1:fTVznVlepH0KK8NyKq8w+U7c2L6jofa27aFX6YGlm38= github.com/cosmos/iavl v0.20.0/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab h1:I9ialKTQo7248V827Bba4OuKPmk+FPzmTVHsLXaIJWw= github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab/go.mod h1:2CwqasX5dSD7Hbp/9b6lhK6BwoBDCBldx7gPKRukR60= -github.com/cosmos/interchain-accounts v0.5.0 h1:bD4U5PzaRbgsGCTwKEShPjKXFxSIp9D+kAqFutYKl1E= -github.com/cosmos/interchain-accounts v0.5.0/go.mod h1:0Pzv5xHq+TVY7ExRIsrzJ33+t22TUoJ1k8UgSxvv1X4= +github.com/cosmos/interchain-accounts v0.5.1 h1:J5ZaYsMc2u4DekKXbDPzv8nu4YD/RSmT0F8dmN7G1oM= +github.com/cosmos/interchain-accounts v0.5.1/go.mod h1:JB3gKbX8geQhxEIrBQtpDco0cyKMUDpVhugb78e5z6U= github.com/cosmos/ledger-cosmos-go v0.13.0 h1:ex0CvCxToSR7j5WjrghPu2Bu9sSXKikjnVvUryNnx4s= github.com/cosmos/ledger-cosmos-go v0.13.0/go.mod h1:ZcqYgnfNJ6lAXe4HPtWgarNEY+B74i+2/8MhZw4ziiI= github.com/cosmos/rosetta-sdk-go v0.10.0 h1:E5RhTruuoA7KTIXUcMicL76cffyeoyvNybzUGSKFTcM= From 9b71740d57e79a1610b62b9a6b12b0cff4325f94 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Mon, 17 Apr 2023 15:36:03 +0100 Subject: [PATCH 13/18] remove CheckIsOpen --- modules/core/03-connection/keeper/keeper.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/modules/core/03-connection/keeper/keeper.go b/modules/core/03-connection/keeper/keeper.go index 7af8b8e110f..4e763c3302b 100644 --- a/modules/core/03-connection/keeper/keeper.go +++ b/modules/core/03-connection/keeper/keeper.go @@ -83,18 +83,6 @@ func (k Keeper) HasConnection(ctx sdk.Context, connectionID string) bool { return store.Has(host.ConnectionKey(connectionID)) } -// CheckIsOpen returns nil if the connection with the given id is OPEN. Otherwise an error is returned. -func (k Keeper) CheckIsOpen(ctx sdk.Context, connectionID string) error { - connection, found := k.GetConnection(ctx, connectionID) - if !found { - return errorsmod.Wrapf(types.ErrConnectionNotFound, "failed to retrieve connection: %s", connectionID) - } - if connection.State != types.OPEN { - return errorsmod.Wrapf(types.ErrInvalidConnectionState, "connectionID (%s) state is not OPEN (got %s)", connectionID, connection.State.String()) - } - return nil -} - // SetConnection sets a connection to the store func (k Keeper) SetConnection(ctx sdk.Context, connectionID string, connection types.ConnectionEnd) { store := ctx.KVStore(k.storeKey) From 7215e84b3c4df18dca8d7b355577e5a6897c3042 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Mon, 17 Apr 2023 15:36:44 +0100 Subject: [PATCH 14/18] updated godoc --- modules/core/04-channel/keeper/keeper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/04-channel/keeper/keeper.go b/modules/core/04-channel/keeper/keeper.go index b944b2f9a62..f23b621c825 100644 --- a/modules/core/04-channel/keeper/keeper.go +++ b/modules/core/04-channel/keeper/keeper.go @@ -577,7 +577,7 @@ func (k Keeper) DeleteUpgradeTimeout(ctx sdk.Context, portID, channelID string) // - there exists at least one valid proposed change to the existing channel fields // - the proposed order is a subset of the existing order // - the proposed connection hops do not exist -// - the proposed version is non-empty (checked in ModifiableUpgradeFields.ValidateBasic()) +// - the proposed version is non-empty (checked in UpgradeFields.ValidateBasic()) // - the proposed connection hops are not open func (k Keeper) ValidateUpgradeFields(ctx sdk.Context, proposedUpgrade types.UpgradeFields, existingChannel types.Channel) error { currentFields := extractUpgradeFields(existingChannel) From 8bb69f349fca839b1faa36e7beb2578eaa51cac5 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Mon, 17 Apr 2023 16:54:28 +0100 Subject: [PATCH 15/18] inverted ordering logic --- modules/core/04-channel/keeper/keeper.go | 2 +- modules/core/04-channel/keeper/upgrade.go | 2 +- modules/core/04-channel/types/channel.go | 5 ++-- modules/core/04-channel/types/channel_test.go | 24 +++++++++---------- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/modules/core/04-channel/keeper/keeper.go b/modules/core/04-channel/keeper/keeper.go index fe91dc5459e..5c6ee0fed80 100644 --- a/modules/core/04-channel/keeper/keeper.go +++ b/modules/core/04-channel/keeper/keeper.go @@ -607,7 +607,7 @@ func (k Keeper) ValidateUpgradeFields(ctx sdk.Context, proposedUpgrade types.Upg return errorsmod.Wrap(types.ErrChannelExists, "existing channel end is identical to proposed upgrade channel end") } - if !currentFields.Ordering.SubsetOf(proposedUpgrade.Ordering) { + if !proposedUpgrade.Ordering.SubsetOf(currentFields.Ordering) { return errorsmod.Wrap(types.ErrInvalidChannelOrdering, "channel ordering must be a subset of the new ordering") } diff --git a/modules/core/04-channel/keeper/upgrade.go b/modules/core/04-channel/keeper/upgrade.go index 90537cc7a4c..73569678355 100644 --- a/modules/core/04-channel/keeper/upgrade.go +++ b/modules/core/04-channel/keeper/upgrade.go @@ -65,7 +65,7 @@ func (k Keeper) ChanUpgradeInit( return 0, "", errorsmod.Wrap(types.ErrInvalidCounterparty, "counterparty port ID and channel ID cannot be upgraded") } - if !channel.Ordering.SubsetOf(proposedUpgradeChannel.Ordering) { + if !proposedUpgradeChannel.Ordering.SubsetOf(channel.Ordering) { return 0, "", errorsmod.Wrap(types.ErrInvalidChannelOrdering, "channel ordering must be a subset of the new ordering") } diff --git a/modules/core/04-channel/types/channel.go b/modules/core/04-channel/types/channel.go index a528b9f3ad9..47217ba45ca 100644 --- a/modules/core/04-channel/types/channel.go +++ b/modules/core/04-channel/types/channel.go @@ -146,10 +146,9 @@ var orderSubsets = map[Order][]Order{ // SubsetOf returns true if the provided Order is a valid subset of Order. func (o Order) SubsetOf(order Order) bool { - if supported, ok := orderSubsets[o]; ok { - return collections.Contains(order, supported) + if supported, ok := orderSubsets[order]; ok { + return collections.Contains(o, supported) } - return false } diff --git a/modules/core/04-channel/types/channel_test.go b/modules/core/04-channel/types/channel_test.go index 47821520ad4..7e134526f5e 100644 --- a/modules/core/04-channel/types/channel_test.go +++ b/modules/core/04-channel/types/channel_test.go @@ -72,9 +72,9 @@ func TestSubsetOf(t *testing.T) { true, }, { - "ordered -> unordered", - types.ORDERED, + "unordered -> ordered", types.UNORDERED, + types.ORDERED, true, }, { @@ -84,33 +84,33 @@ func TestSubsetOf(t *testing.T) { true, }, { - "unordered -> ordered", - types.UNORDERED, + "ordered -> unordered", types.ORDERED, + types.UNORDERED, false, }, { - "none -> ordered", - types.NONE, + "ordered -> none", types.ORDERED, + types.NONE, false, }, { - "none -> unordered", - types.NONE, + "unordered -> none", types.UNORDERED, + types.NONE, false, }, { - "ordered -> none", - types.ORDERED, + "none -> ordered", types.NONE, + types.ORDERED, false, }, { - "unordered -> none", - types.UNORDERED, + "none -> unordered", types.NONE, + types.UNORDERED, false, }, } From b514798ef5913e2558b3470fb3eed589bedd3a90 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Mon, 17 Apr 2023 16:57:33 +0100 Subject: [PATCH 16/18] renamed from existingChannel to currentChannel --- modules/core/04-channel/keeper/keeper.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/04-channel/keeper/keeper.go b/modules/core/04-channel/keeper/keeper.go index 5c6ee0fed80..6ae81dba5ee 100644 --- a/modules/core/04-channel/keeper/keeper.go +++ b/modules/core/04-channel/keeper/keeper.go @@ -600,8 +600,8 @@ func (k Keeper) SetUpgrade(ctx sdk.Context, portID, channelID string, upgrade ty // - the proposed connection hops do not exist // - the proposed version is non-empty (checked in UpgradeFields.ValidateBasic()) // - the proposed connection hops are not open -func (k Keeper) ValidateUpgradeFields(ctx sdk.Context, proposedUpgrade types.UpgradeFields, existingChannel types.Channel) error { - currentFields := extractUpgradeFields(existingChannel) +func (k Keeper) ValidateUpgradeFields(ctx sdk.Context, proposedUpgrade types.UpgradeFields, currentChannel types.Channel) error { + currentFields := extractUpgradeFields(currentChannel) if reflect.DeepEqual(proposedUpgrade, currentFields) { return errorsmod.Wrap(types.ErrChannelExists, "existing channel end is identical to proposed upgrade channel end") From aaaf02da62100a059df7e2e243562ffa0d12c717 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Tue, 18 Apr 2023 09:48:21 +0100 Subject: [PATCH 17/18] removed unused verify functions --- modules/core/03-connection/keeper/verify.go | 80 -------- .../core/03-connection/keeper/verify_test.go | 187 +----------------- 2 files changed, 1 insertion(+), 266 deletions(-) diff --git a/modules/core/03-connection/keeper/verify.go b/modules/core/03-connection/keeper/verify.go index f8afcf74db7..e77601bcc81 100644 --- a/modules/core/03-connection/keeper/verify.go +++ b/modules/core/03-connection/keeper/verify.go @@ -364,86 +364,6 @@ func (k Keeper) VerifyNextSequenceRecv( return nil } -// VerifyChannelUpgradeSequence verifies a proof of the upgrade sequence number to be -// used during channel upgrades. -func (k Keeper) VerifyChannelUpgradeSequence( - ctx sdk.Context, - connection exported.ConnectionI, - height exported.Height, - proof []byte, - portID, - channelID string, - upgradeSequence uint64, -) error { - clientID := connection.GetClientID() - clientState, clientStore, err := k.getClientStateAndVerificationStore(ctx, clientID) - if err != nil { - return err - } - - if status := k.clientKeeper.GetClientStatus(ctx, clientState, clientID); status != exported.Active { - return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) - } - - merklePath := commitmenttypes.NewMerklePath(host.ChannelUpgradeSequencePath(portID, channelID)) - merklePath, err = commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) - if err != nil { - return err - } - - if err := clientState.VerifyMembership( - ctx, clientStore, k.cdc, height, - 0, 0, // skip delay period checks for non-packet processing verification - proof, merklePath, sdk.Uint64ToBigEndian(upgradeSequence), - ); err != nil { - return errorsmod.Wrapf(err, "failed upgrade sequence verification for client (%s)", clientID) - } - - return nil -} - -// VerifyChannelUpgradeTimeout verifies the proof that a particular timeout has been stored in the upgrade timeout path. -func (k Keeper) VerifyChannelUpgradeTimeout( - ctx sdk.Context, - connection exported.ConnectionI, - height exported.Height, - proof []byte, - portID, - channelID string, - upgradeTimeout channeltypes.UpgradeTimeout, -) error { - clientID := connection.GetClientID() - clientState, clientStore, err := k.getClientStateAndVerificationStore(ctx, clientID) - if err != nil { - return err - } - - if status := k.clientKeeper.GetClientStatus(ctx, clientState, clientID); status != exported.Active { - return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) - } - - merklePath := commitmenttypes.NewMerklePath(host.ChannelUpgradeTimeoutPath(portID, channelID)) - merklePath, err = commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) - if err != nil { - return err - } - - bz, err := k.cdc.Marshal(&upgradeTimeout) - if err != nil { - return err - } - - if err := clientState.VerifyMembership( - ctx, clientStore, k.cdc, height, - 0, 0, // skip delay period checks for non-packet processing verification - proof, merklePath, bz, - ); err != nil { - return errorsmod.Wrapf(err, "failed upgrade timeout verification for client (%s) on channel (%s)", clientID, channelID) - } - - return nil -} - // VerifyChannelUpgradeError verifies a proof of the provided upgrade error receipt. func (k Keeper) VerifyChannelUpgradeError( ctx sdk.Context, diff --git a/modules/core/03-connection/keeper/verify_test.go b/modules/core/03-connection/keeper/verify_test.go index 9edc90566f2..9606e7fd2fb 100644 --- a/modules/core/03-connection/keeper/verify_test.go +++ b/modules/core/03-connection/keeper/verify_test.go @@ -682,191 +682,6 @@ func (suite *KeeperTestSuite) TestVerifyNextSequenceRecv() { } } -func (suite *KeeperTestSuite) TestVerifyUpgradeSequence() { - var ( - path *ibctesting.Path - expectedUpgradeSequence uint64 - ) - - cases := []struct { - name string - malleate func() - expPass bool - }{ - { - name: "success", - malleate: func() {}, - expPass: true, - }, - { - name: "fails with different upgrade sequence", - malleate: func() { - expectedUpgradeSequence = 10 - }, - expPass: false, - }, - { - name: "fails when client state is frozen", - malleate: func() { - clientState := path.EndpointB.GetClientState().(*ibctm.ClientState) - clientState.FrozenHeight = clienttypes.NewHeight(0, 1) - path.EndpointB.SetClientState(clientState) - }, - expPass: false, - }, - { - name: "fails with bad client id", - malleate: func() { - connection := path.EndpointB.GetConnection() - connection.ClientId = ibctesting.InvalidID - path.EndpointB.SetConnection(connection) - }, - expPass: false, - }, - { - name: "verification fails when the key does not exist", - malleate: func() { - storeKey := path.EndpointA.Chain.GetSimApp().GetKey(exported.StoreKey) - kvStore := path.EndpointA.Chain.GetContext().KVStore(storeKey) - kvStore.Delete(host.ChannelUpgradeSequenceKey(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)) - suite.coordinator.CommitBlock(suite.chainA) - }, - expPass: false, - }, - } - - for _, tc := range cases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - expectedUpgradeSequence = 1 - - path = ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.Setup(path) - - // specify a new version to upgrade to. - path.EndpointA.ChannelConfig.Version = fmt.Sprintf("%s-v2", ibcmock.Version) - - err := path.EndpointA.ChanUpgradeInit(path.EndpointB.Chain.GetTimeoutHeight(), 0) - suite.Require().NoError(err) - - tc.malleate() - - upgradeSequenceKey := host.ChannelUpgradeSequenceKey(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - proof, proofHeight := suite.chainA.QueryProof(upgradeSequenceKey) - - err = suite.chainB.GetSimApp().IBCKeeper.ConnectionKeeper.VerifyChannelUpgradeSequence(suite.chainB.GetContext(), path.EndpointB.GetConnection(), proofHeight, proof, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, expectedUpgradeSequence) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -func (suite *KeeperTestSuite) TestVerifyUpgradeTimeout() { - var ( - path *ibctesting.Path - upgradeTimeout channeltypes.UpgradeTimeout - ) - - cases := []struct { - name string - malleate func() - expPass bool - }{ - { - name: "success", - malleate: func() { - }, - expPass: true, - }, - { - name: "fails when client state is frozen", - malleate: func() { - clientState := path.EndpointB.GetClientState().(*ibctm.ClientState) - clientState.FrozenHeight = clienttypes.NewHeight(0, 1) - path.EndpointB.SetClientState(clientState) - }, - expPass: false, - }, - { - name: "fails with bad client id", - malleate: func() { - connection := path.EndpointB.GetConnection() - connection.ClientId = ibctesting.InvalidID - path.EndpointB.SetConnection(connection) - }, - expPass: false, - }, - { - name: "verification fails when the key does not exist", - malleate: func() { - storeKey := path.EndpointA.Chain.GetSimApp().GetKey(exported.StoreKey) - kvStore := path.EndpointA.Chain.GetContext().KVStore(storeKey) - kvStore.Delete(host.ChannelUpgradeTimeoutKey(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID)) - suite.coordinator.CommitBlock(suite.chainA) - }, - expPass: false, - }, - } - - for _, tc := range cases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - height := clienttypes.GetSelfHeight(suite.chainB.GetContext()) - timestamp := uint64(suite.chainB.GetContext().BlockTime().UnixNano()) - - upgradeTimeout = channeltypes.UpgradeTimeout{ - TimeoutHeight: height, - TimeoutTimestamp: timestamp, - } - - path = ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.Setup(path) - - // specify a new version to upgrade to. - path.EndpointA.ChannelConfig.Version = fmt.Sprintf("%s-v2", ibcmock.Version) - - // chain A initiates an upgrade and stores the timeout height/timestamp by which point chain B must have proceeded - // to the TRY step. In this case, we set it to the current height/timestamp to ensure timeout. - err := path.EndpointA.ChanUpgradeInit(height, timestamp) - suite.Require().NoError(err) - - tc.malleate() - - upgradeTimeoutKey := host.ChannelUpgradeTimeoutKey(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) - proof, proofHeight := suite.chainA.QueryProof(upgradeTimeoutKey) - - // The VerifyChannelUpgrade step is called by chain B, and will prove the timeout values set by chain A - // have not passed on chain B. - err = suite.chainB.GetSimApp().IBCKeeper.ConnectionKeeper.VerifyChannelUpgradeTimeout( - suite.chainB.GetContext(), - path.EndpointB.GetConnection(), - proofHeight, - proof, - // this should be the counterparty port and channel id (chain A) - path.EndpointA.ChannelConfig.PortID, - path.EndpointA.ChannelID, - upgradeTimeout, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - func (suite *KeeperTestSuite) TestVerifyUpgradeErrorReceipt() { var ( path *ibctesting.Path @@ -1055,7 +870,7 @@ func (suite *KeeperTestSuite) TestVerifyUpgrade() { expPass: false, }, { - name: "verification fails when the ordering is different", + name: "fails when the upgrade field is different", malleate: func() { upgrade.Fields.Ordering = channeltypes.ORDERED }, From 75bc62d5a4e74bfbf44cb99c5d19fe7748cb0ddc Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Tue, 18 Apr 2023 10:37:39 +0100 Subject: [PATCH 18/18] addressing PR feedback --- modules/core/03-connection/keeper/verify_test.go | 2 +- modules/core/04-channel/types/upgrade.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/core/03-connection/keeper/verify_test.go b/modules/core/03-connection/keeper/verify_test.go index 9606e7fd2fb..55e2cad1679 100644 --- a/modules/core/03-connection/keeper/verify_test.go +++ b/modules/core/03-connection/keeper/verify_test.go @@ -888,7 +888,7 @@ func (suite *KeeperTestSuite) TestVerifyUpgrade() { suite.coordinator.Setup(path) upgrade = channeltypes.NewUpgrade( - channeltypes.NewModifiableUpgradeFields(channeltypes.UNORDERED, []string{path.EndpointA.ConnectionID}, "v1.0.0"), + channeltypes.NewUpgradeFields(channeltypes.UNORDERED, []string{path.EndpointA.ConnectionID}, "v1.0.0"), channeltypes.NewUpgradeTimeout(clienttypes.ZeroHeight(), 100000), 0, ) diff --git a/modules/core/04-channel/types/upgrade.go b/modules/core/04-channel/types/upgrade.go index be623c405cc..d506f6fb99b 100644 --- a/modules/core/04-channel/types/upgrade.go +++ b/modules/core/04-channel/types/upgrade.go @@ -18,8 +18,8 @@ func NewUpgrade(upgradeFields UpgradeFields, timeout UpgradeTimeout, latestPacke } } -// NewModifiableUpgradeFields returns a new ModifiableUpgradeFields instance. -func NewModifiableUpgradeFields(ordering Order, connectionHops []string, version string) UpgradeFields { +// NewUpgradeFields returns a new ModifiableUpgradeFields instance. +func NewUpgradeFields(ordering Order, connectionHops []string, version string) UpgradeFields { return UpgradeFields{ Ordering: ordering, ConnectionHops: connectionHops,