From 8a7f2bfeb9a499c79440382131c66701cf737322 Mon Sep 17 00:00:00 2001 From: jgo121 Date: Tue, 27 Sep 2022 09:14:23 +0800 Subject: [PATCH 01/44] ibc transfer authz work --- modules/apps/transfer/types/authz.pb.go | 636 ++++++++++++++++++ modules/apps/transfer/types/codec.go | 6 + modules/apps/transfer/types/transfer_authz.go | 89 +++ .../transfer/types/transfer_authz_test.go | 79 +++ .../ibc/applications/transfer/v2/authz.proto | 28 + 5 files changed, 838 insertions(+) create mode 100644 modules/apps/transfer/types/authz.pb.go create mode 100644 modules/apps/transfer/types/transfer_authz.go create mode 100644 modules/apps/transfer/types/transfer_authz_test.go create mode 100644 proto/ibc/applications/transfer/v2/authz.proto diff --git a/modules/apps/transfer/types/authz.pb.go b/modules/apps/transfer/types/authz.pb.go new file mode 100644 index 00000000000..158c70475eb --- /dev/null +++ b/modules/apps/transfer/types/authz.pb.go @@ -0,0 +1,636 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/applications/transfer/v2/authz.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + _ "github.com/regen-network/cosmos-proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type PortChannelAmount struct { + // the port on which the packet will be sent + SourcePort string `protobuf:"bytes,1,opt,name=source_port,json=sourcePort,proto3" json:"source_port,omitempty" yaml:"source_port"` + // the channel by which the packet will be sent + SourceChannel string `protobuf:"bytes,2,opt,name=source_channel,json=sourceChannel,proto3" json:"source_channel,omitempty" yaml:"source_channel"` + // spend limitation on the channel + SpendLimit github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=spend_limit,json=spendLimit,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"spend_limit"` +} + +func (m *PortChannelAmount) Reset() { *m = PortChannelAmount{} } +func (m *PortChannelAmount) String() string { return proto.CompactTextString(m) } +func (*PortChannelAmount) ProtoMessage() {} +func (*PortChannelAmount) Descriptor() ([]byte, []int) { + return fileDescriptor_c4c17169771443ae, []int{0} +} +func (m *PortChannelAmount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PortChannelAmount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PortChannelAmount.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 *PortChannelAmount) XXX_Merge(src proto.Message) { + xxx_messageInfo_PortChannelAmount.Merge(m, src) +} +func (m *PortChannelAmount) XXX_Size() int { + return m.Size() +} +func (m *PortChannelAmount) XXX_DiscardUnknown() { + xxx_messageInfo_PortChannelAmount.DiscardUnknown(m) +} + +var xxx_messageInfo_PortChannelAmount proto.InternalMessageInfo + +func (m *PortChannelAmount) GetSourcePort() string { + if m != nil { + return m.SourcePort + } + return "" +} + +func (m *PortChannelAmount) GetSourceChannel() string { + if m != nil { + return m.SourceChannel + } + return "" +} + +func (m *PortChannelAmount) GetSpendLimit() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.SpendLimit + } + return nil +} + +// TransferAuthorization allows the grantee to spend up to spend_limit coins from +// the granter's account for ibc transfer on a specific channel +type TransferAuthorization struct { + // port and channel amounts + Allocations []PortChannelAmount `protobuf:"bytes,1,rep,name=allocations,proto3" json:"allocations"` +} + +func (m *TransferAuthorization) Reset() { *m = TransferAuthorization{} } +func (m *TransferAuthorization) String() string { return proto.CompactTextString(m) } +func (*TransferAuthorization) ProtoMessage() {} +func (*TransferAuthorization) Descriptor() ([]byte, []int) { + return fileDescriptor_c4c17169771443ae, []int{1} +} +func (m *TransferAuthorization) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TransferAuthorization) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TransferAuthorization.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 *TransferAuthorization) XXX_Merge(src proto.Message) { + xxx_messageInfo_TransferAuthorization.Merge(m, src) +} +func (m *TransferAuthorization) XXX_Size() int { + return m.Size() +} +func (m *TransferAuthorization) XXX_DiscardUnknown() { + xxx_messageInfo_TransferAuthorization.DiscardUnknown(m) +} + +var xxx_messageInfo_TransferAuthorization proto.InternalMessageInfo + +func (m *TransferAuthorization) GetAllocations() []PortChannelAmount { + if m != nil { + return m.Allocations + } + return nil +} + +func init() { + proto.RegisterType((*PortChannelAmount)(nil), "ibc.applications.transfer.v2.PortChannelAmount") + proto.RegisterType((*TransferAuthorization)(nil), "ibc.applications.transfer.v2.TransferAuthorization") +} + +func init() { + proto.RegisterFile("ibc/applications/transfer/v2/authz.proto", fileDescriptor_c4c17169771443ae) +} + +var fileDescriptor_c4c17169771443ae = []byte{ + // 414 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x52, 0x41, 0x6b, 0x14, 0x31, + 0x14, 0xde, 0x58, 0x11, 0xcc, 0x52, 0xa1, 0x83, 0x95, 0xdd, 0x22, 0xb3, 0x65, 0x4e, 0x7b, 0xd9, + 0xc4, 0xae, 0x60, 0xa1, 0x27, 0x3b, 0xbd, 0x7a, 0xd0, 0x41, 0x10, 0xbc, 0x2c, 0x99, 0x6c, 0x9c, + 0x09, 0x66, 0xf2, 0x86, 0x49, 0x66, 0xa0, 0xbd, 0xfa, 0x07, 0xfc, 0x1d, 0x9e, 0xfd, 0x11, 0x3d, + 0x16, 0x4f, 0x9e, 0x56, 0xd9, 0xfd, 0x07, 0x3d, 0x7b, 0x90, 0x49, 0x22, 0x4c, 0x29, 0x78, 0x9a, + 0xc9, 0x7b, 0xdf, 0xf7, 0xe5, 0x7d, 0xdf, 0x0b, 0x9e, 0xcb, 0x9c, 0x53, 0x56, 0xd7, 0x4a, 0x72, + 0x66, 0x25, 0x68, 0x43, 0x6d, 0xc3, 0xb4, 0xf9, 0x24, 0x1a, 0xda, 0x2d, 0x29, 0x6b, 0x6d, 0x79, + 0x45, 0xea, 0x06, 0x2c, 0x44, 0xcf, 0x65, 0xce, 0xc9, 0x10, 0x49, 0xfe, 0x21, 0x49, 0xb7, 0x3c, + 0x9a, 0x72, 0x30, 0x15, 0x98, 0x95, 0xc3, 0x52, 0x7f, 0xf0, 0xc4, 0xa3, 0xa7, 0x05, 0x14, 0xe0, + 0xeb, 0xfd, 0x5f, 0xa8, 0xc6, 0x1e, 0x43, 0x73, 0x66, 0x04, 0xed, 0x4e, 0x72, 0x61, 0xd9, 0x09, + 0xe5, 0x20, 0xb5, 0xef, 0x27, 0x7f, 0x10, 0x3e, 0x78, 0x0b, 0x8d, 0xbd, 0x28, 0x99, 0xd6, 0x42, + 0x9d, 0x57, 0xd0, 0x6a, 0x1b, 0x9d, 0xe2, 0xb1, 0x81, 0xb6, 0xe1, 0x62, 0x55, 0x43, 0x63, 0x27, + 0xe8, 0x18, 0xcd, 0x1f, 0xa7, 0xcf, 0x6e, 0x37, 0xb3, 0xe8, 0x92, 0x55, 0xea, 0x2c, 0x19, 0x34, + 0x93, 0x0c, 0xfb, 0x53, 0xaf, 0x12, 0xbd, 0xc6, 0x4f, 0x42, 0x8f, 0x7b, 0xc1, 0xc9, 0x03, 0xc7, + 0x9d, 0xde, 0x6e, 0x66, 0x87, 0x77, 0xb8, 0xa1, 0x9f, 0x64, 0xfb, 0xbe, 0x10, 0x06, 0x88, 0x14, + 0x1e, 0x9b, 0x5a, 0xe8, 0xf5, 0x4a, 0xc9, 0x4a, 0xda, 0xc9, 0xde, 0xf1, 0xde, 0x7c, 0xbc, 0x9c, + 0x92, 0x60, 0xb5, 0xb7, 0x41, 0x82, 0x0d, 0x72, 0x01, 0x52, 0xa7, 0x2f, 0xae, 0x37, 0xb3, 0xd1, + 0xb7, 0x5f, 0xb3, 0x79, 0x21, 0x6d, 0xd9, 0xe6, 0x84, 0x43, 0x15, 0x72, 0x09, 0x9f, 0x85, 0x59, + 0x7f, 0xa6, 0xf6, 0xb2, 0x16, 0xc6, 0x11, 0x4c, 0x86, 0x9d, 0xfe, 0x9b, 0x5e, 0x3e, 0xf9, 0x82, + 0xf0, 0xe1, 0xfb, 0x90, 0xef, 0x79, 0x6b, 0x4b, 0x68, 0xe4, 0x95, 0x8b, 0x3d, 0xfa, 0x80, 0xc7, + 0x4c, 0x29, 0x08, 0x4b, 0x98, 0x20, 0x37, 0x07, 0x25, 0xff, 0xdb, 0x0e, 0xb9, 0x17, 0x64, 0xfa, + 0xb0, 0x9f, 0x2e, 0x1b, 0x2a, 0x9d, 0x1d, 0xfc, 0xf8, 0xbe, 0xd8, 0xbf, 0x73, 0x57, 0xfa, 0xee, + 0x7a, 0x1b, 0xa3, 0x9b, 0x6d, 0x8c, 0x7e, 0x6f, 0x63, 0xf4, 0x75, 0x17, 0x8f, 0x6e, 0x76, 0xf1, + 0xe8, 0xe7, 0x2e, 0x1e, 0x7d, 0x3c, 0xbd, 0xef, 0x4a, 0xe6, 0x7c, 0x51, 0x00, 0xed, 0x5e, 0xd1, + 0x0a, 0xd6, 0xad, 0x12, 0xa6, 0x7f, 0x57, 0x83, 0xf7, 0xe4, 0xac, 0xe6, 0x8f, 0xdc, 0x7a, 0x5f, + 0xfe, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x2f, 0x11, 0x7e, 0x74, 0x79, 0x02, 0x00, 0x00, +} + +func (m *PortChannelAmount) 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 *PortChannelAmount) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PortChannelAmount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.SpendLimit) > 0 { + for iNdEx := len(m.SpendLimit) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.SpendLimit[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAuthz(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.SourceChannel) > 0 { + i -= len(m.SourceChannel) + copy(dAtA[i:], m.SourceChannel) + i = encodeVarintAuthz(dAtA, i, uint64(len(m.SourceChannel))) + i-- + dAtA[i] = 0x12 + } + if len(m.SourcePort) > 0 { + i -= len(m.SourcePort) + copy(dAtA[i:], m.SourcePort) + i = encodeVarintAuthz(dAtA, i, uint64(len(m.SourcePort))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TransferAuthorization) 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 *TransferAuthorization) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TransferAuthorization) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Allocations) > 0 { + for iNdEx := len(m.Allocations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Allocations[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAuthz(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintAuthz(dAtA []byte, offset int, v uint64) int { + offset -= sovAuthz(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *PortChannelAmount) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SourcePort) + if l > 0 { + n += 1 + l + sovAuthz(uint64(l)) + } + l = len(m.SourceChannel) + if l > 0 { + n += 1 + l + sovAuthz(uint64(l)) + } + if len(m.SpendLimit) > 0 { + for _, e := range m.SpendLimit { + l = e.Size() + n += 1 + l + sovAuthz(uint64(l)) + } + } + return n +} + +func (m *TransferAuthorization) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Allocations) > 0 { + for _, e := range m.Allocations { + l = e.Size() + n += 1 + l + sovAuthz(uint64(l)) + } + } + return n +} + +func sovAuthz(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAuthz(x uint64) (n int) { + return sovAuthz(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *PortChannelAmount) 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 ErrIntOverflowAuthz + } + 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: PortChannelAmount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PortChannelAmount: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourcePort", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + 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 ErrInvalidLengthAuthz + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SourcePort = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourceChannel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + 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 ErrInvalidLengthAuthz + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SourceChannel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SpendLimit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAuthz + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SpendLimit = append(m.SpendLimit, types.Coin{}) + if err := m.SpendLimit[len(m.SpendLimit)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuthz(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAuthz + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TransferAuthorization) 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 ErrIntOverflowAuthz + } + 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: TransferAuthorization: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TransferAuthorization: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Allocations", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAuthz + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Allocations = append(m.Allocations, PortChannelAmount{}) + if err := m.Allocations[len(m.Allocations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuthz(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAuthz + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAuthz(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthAuthz + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAuthz + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAuthz + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAuthz = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAuthz = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAuthz = fmt.Errorf("proto: unexpected end of group") +) diff --git a/modules/apps/transfer/types/codec.go b/modules/apps/transfer/types/codec.go index 24ad7e5a902..e8bac8f1db2 100644 --- a/modules/apps/transfer/types/codec.go +++ b/modules/apps/transfer/types/codec.go @@ -5,6 +5,7 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" + "github.com/cosmos/cosmos-sdk/x/authz" ) // RegisterLegacyAminoCodec registers the necessary x/ibc transfer interfaces and concrete types @@ -18,6 +19,11 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { func RegisterInterfaces(registry codectypes.InterfaceRegistry) { registry.RegisterImplementations((*sdk.Msg)(nil), &MsgTransfer{}) + registry.RegisterImplementations( + (*authz.Authorization)(nil), + &TransferAuthorization{}, + ) + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) } diff --git a/modules/apps/transfer/types/transfer_authz.go b/modules/apps/transfer/types/transfer_authz.go new file mode 100644 index 00000000000..d1c5d0e9fbb --- /dev/null +++ b/modules/apps/transfer/types/transfer_authz.go @@ -0,0 +1,89 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/authz" + host "github.com/cosmos/ibc-go/v6/modules/core/24-host" + + "golang.org/x/exp/slices" +) + +var ( + _ authz.Authorization = &TransferAuthorization{} +) + +// NewTransferAuthorization creates a new TransferAuthorization object. +func NewTransferAuthorization(sourcePorts, sourceChannels []string, spendLimits []sdk.Coins) *TransferAuthorization { + allocations := []PortChannelAmount{} + for index := range sourcePorts { + allocations = append(allocations, PortChannelAmount{ + SourcePort: sourcePorts[index], + SourceChannel: sourceChannels[index], + SpendLimit: spendLimits[index], + }) + } + return &TransferAuthorization{ + Allocations: allocations, + } +} + +// MsgTypeURL implements Authorization.MsgTypeURL. +func (a TransferAuthorization) MsgTypeURL() string { + return sdk.MsgTypeURL(&MsgTransfer{}) +} + +// Accept implements Authorization.Accept. +func (a TransferAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.AcceptResponse, error) { + mTransfer, ok := msg.(*MsgTransfer) + if !ok { + return authz.AcceptResponse{}, sdkerrors.ErrInvalidType.Wrap("type mismatch") + } + + for index, allocation := range a.Allocations { + if allocation.SourceChannel == mTransfer.SourceChannel && allocation.SourcePort == mTransfer.SourcePort { + limitLeft, isNegative := allocation.SpendLimit.SafeSub(mTransfer.Token) + if isNegative { + return authz.AcceptResponse{}, sdkerrors.ErrInsufficientFunds.Wrapf("requested amount is more than spend limit") + } + if limitLeft.IsZero() { + a.Allocations = slices.Delete(a.Allocations, index, index+1) + if len(a.Allocations) == 0 { + return authz.AcceptResponse{Accept: true, Delete: true}, nil + } + return authz.AcceptResponse{Accept: true, Delete: false, Updated: &TransferAuthorization{ + Allocations: a.Allocations, + }}, nil + } + a.Allocations[index] = PortChannelAmount{ + SourcePort: allocation.SourcePort, + SourceChannel: allocation.SourceChannel, + SpendLimit: limitLeft, + } + + return authz.AcceptResponse{Accept: true, Delete: false, Updated: &TransferAuthorization{ + Allocations: a.Allocations, + }}, nil + } + } + return authz.AcceptResponse{}, sdkerrors.ErrInsufficientFunds.Wrapf("requested port and channel allocation does not exist") +} + +// ValidateBasic implements Authorization.ValidateBasic. +func (a TransferAuthorization) ValidateBasic() error { + for _, allocation := range a.Allocations { + if allocation.SpendLimit == nil { + return sdkerrors.ErrInvalidCoins.Wrap("spend limit cannot be nil") + } + if !allocation.SpendLimit.IsAllPositive() { + return sdkerrors.ErrInvalidCoins.Wrapf("spend limit cannot be negitive") + } + if err := host.PortIdentifierValidator(allocation.SourcePort); err != nil { + return sdkerrors.Wrap(err, "invalid source port ID") + } + if err := host.ChannelIdentifierValidator(allocation.SourceChannel); err != nil { + return sdkerrors.Wrap(err, "invalid source channel ID") + } + } + return nil +} diff --git a/modules/apps/transfer/types/transfer_authz_test.go b/modules/apps/transfer/types/transfer_authz_test.go new file mode 100644 index 00000000000..838b26ee36c --- /dev/null +++ b/modules/apps/transfer/types/transfer_authz_test.go @@ -0,0 +1,79 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + sourcePort = "port" + sourceChannel = "channel-100" + sourcePort2 = "port2" + sourceChannel2 = "channel-101" + coins1000 = sdk.Coins{sdk.NewCoin("stake", sdk.NewInt(1000))} + coins500 = sdk.Coins{sdk.NewCoin("stake", sdk.NewInt(500))} + coin1000 = sdk.NewCoin("stake", sdk.NewInt(1000)) + coin500 = sdk.NewCoin("stake", sdk.NewInt(500)) + fromAddr = sdk.AccAddress("_____from _____") + toAddr = sdk.AccAddress("_______to________") +) + +func TestTransferAuthorization(t *testing.T) { + app := simapp.Setup(t, false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + authorization := NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins1000}) + + t.Log("verify authorization returns valid method name") + require.Equal(t, authorization.MsgTypeURL(), "/ibc.applications.transfer.v1.MsgTransfer") + require.NoError(t, authorization.ValidateBasic()) + transfer := NewMsgTransfer(sourcePort, sourceChannel, coin1000, fromAddr.String(), toAddr.String(), timeoutHeight, 0) + require.NoError(t, authorization.ValidateBasic()) + + t.Log("verify updated authorization returns nil") + resp, err := authorization.Accept(ctx, transfer) + require.NoError(t, err) + require.True(t, resp.Delete) + require.Nil(t, resp.Updated) + + t.Log("verify updated authorization returns remaining spent limit") + authorization = NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins1000}) + require.Equal(t, authorization.MsgTypeURL(), "/ibc.applications.transfer.v1.MsgTransfer") + require.NoError(t, authorization.ValidateBasic()) + transfer = NewMsgTransfer(sourcePort, sourceChannel, coin500, fromAddr.String(), toAddr.String(), timeoutHeight, 0) + require.NoError(t, authorization.ValidateBasic()) + resp, err = authorization.Accept(ctx, transfer) + require.NoError(t, err) + require.False(t, resp.Delete) + require.NotNil(t, resp.Updated) + sendAuth := NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins500}) + require.Equal(t, sendAuth.String(), resp.Updated.String()) + + t.Log("expect updated authorization nil after spending remaining amount") + resp, err = resp.Updated.Accept(ctx, transfer) + require.NoError(t, err) + require.True(t, resp.Delete) + require.Nil(t, resp.Updated) + + t.Log("expect error when spend limit for specific port and channel is not set") + authorization = NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins1000}) + transfer = NewMsgTransfer(sourcePort2, sourceChannel2, coin500, fromAddr.String(), toAddr.String(), timeoutHeight, 0) + _, err = authorization.Accept(ctx, transfer) + require.Error(t, err) + + t.Log("expect removing only 1 allocation if spend limit is finalized for the port") + authorization = NewTransferAuthorization( + []string{sourcePort, sourcePort2}, + []string{sourceChannel, sourceChannel2}, + []sdk.Coins{coins1000, coins1000}) + transfer = NewMsgTransfer(sourcePort, sourceChannel, coin1000, fromAddr.String(), toAddr.String(), timeoutHeight, 0) + resp, err = authorization.Accept(ctx, transfer) + require.NoError(t, err) + require.NotNil(t, resp.Updated) + require.Equal(t, resp.Updated, NewTransferAuthorization([]string{sourcePort2}, []string{sourceChannel2}, []sdk.Coins{coins1000})) + require.False(t, resp.Delete) +} diff --git a/proto/ibc/applications/transfer/v2/authz.proto b/proto/ibc/applications/transfer/v2/authz.proto new file mode 100644 index 00000000000..cba7ebed0c9 --- /dev/null +++ b/proto/ibc/applications/transfer/v2/authz.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; + +package ibc.applications.transfer.v2; + +option go_package = "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"; + +import "cosmos_proto/cosmos.proto"; +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +message PortChannelAmount { + // the port on which the packet will be sent + string source_port = 1 [(gogoproto.moretags) = "yaml:\"source_port\""]; + // the channel by which the packet will be sent + string source_channel = 2 [(gogoproto.moretags) = "yaml:\"source_channel\""]; + // spend limitation on the channel + repeated cosmos.base.v1beta1.Coin spend_limit = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// TransferAuthorization allows the grantee to spend up to spend_limit coins from +// the granter's account for ibc transfer on a specific channel +message TransferAuthorization { + option (cosmos_proto.implements_interface) = "Authorization"; + + // port and channel amounts + repeated PortChannelAmount allocations = 1 [(gogoproto.nullable) = false]; +} From f84bb92f94000c68f5b2de9c3063130fbed5c656 Mon Sep 17 00:00:00 2001 From: jgo121 Date: Mon, 3 Oct 2022 22:33:34 +0800 Subject: [PATCH 02/44] add allowed address restriction for ibc transfer authz --- docs/ibc/proto-docs.md | 54 +++++++++ modules/apps/transfer/types/authz.pb.go | 112 +++++++++++++----- modules/apps/transfer/types/transfer_authz.go | 14 +++ .../ibc/applications/transfer/v2/authz.proto | 2 + 4 files changed, 155 insertions(+), 27 deletions(-) diff --git a/docs/ibc/proto-docs.md b/docs/ibc/proto-docs.md index 8183045f4fc..00b09cad82f 100644 --- a/docs/ibc/proto-docs.md +++ b/docs/ibc/proto-docs.md @@ -154,6 +154,10 @@ - [Msg](#ibc.applications.transfer.v1.Msg) +- [ibc/applications/transfer/v2/authz.proto](#ibc/applications/transfer/v2/authz.proto) + - [PortChannelAmount](#ibc.applications.transfer.v2.PortChannelAmount) + - [TransferAuthorization](#ibc.applications.transfer.v2.TransferAuthorization) + - [ibc/applications/transfer/v2/packet.proto](#ibc/applications/transfer/v2/packet.proto) - [FungibleTokenPacketData](#ibc.applications.transfer.v2.FungibleTokenPacketData) @@ -2291,6 +2295,56 @@ Msg defines the ibc/transfer Msg service. + +

Top

+ +## ibc/applications/transfer/v2/authz.proto + + + + + +### PortChannelAmount + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `source_port` | [string](#string) | | the port on which the packet will be sent | +| `source_channel` | [string](#string) | | the channel by which the packet will be sent | +| `spend_limit` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | spend limitation on the channel | +| `allowed_addresses` | [string](#string) | repeated | | + + + + + + + + +### TransferAuthorization +TransferAuthorization allows the grantee to spend up to spend_limit coins from +the granter's account for ibc transfer on a specific channel + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `allocations` | [PortChannelAmount](#ibc.applications.transfer.v2.PortChannelAmount) | repeated | port and channel amounts | + + + + + + + + + + + + + + +

Top

diff --git a/modules/apps/transfer/types/authz.pb.go b/modules/apps/transfer/types/authz.pb.go index 158c70475eb..db9a9e3dbc3 100644 --- a/modules/apps/transfer/types/authz.pb.go +++ b/modules/apps/transfer/types/authz.pb.go @@ -33,6 +33,8 @@ type PortChannelAmount struct { SourceChannel string `protobuf:"bytes,2,opt,name=source_channel,json=sourceChannel,proto3" json:"source_channel,omitempty" yaml:"source_channel"` // spend limitation on the channel SpendLimit github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=spend_limit,json=spendLimit,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"spend_limit"` + // allowed addresses to be sent via transfer message + AllowedAddresses []string `protobuf:"bytes,4,rep,name=allowed_addresses,json=allowedAddresses,proto3" json:"allowed_addresses,omitempty"` } func (m *PortChannelAmount) Reset() { *m = PortChannelAmount{} } @@ -89,6 +91,13 @@ func (m *PortChannelAmount) GetSpendLimit() github_com_cosmos_cosmos_sdk_types.C return nil } +func (m *PortChannelAmount) GetAllowedAddresses() []string { + if m != nil { + return m.AllowedAddresses + } + return nil +} + // TransferAuthorization allows the grantee to spend up to spend_limit coins from // the granter's account for ibc transfer on a specific channel type TransferAuthorization struct { @@ -146,33 +155,35 @@ func init() { } var fileDescriptor_c4c17169771443ae = []byte{ - // 414 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x52, 0x41, 0x6b, 0x14, 0x31, - 0x14, 0xde, 0x58, 0x11, 0xcc, 0x52, 0xa1, 0x83, 0x95, 0xdd, 0x22, 0xb3, 0x65, 0x4e, 0x7b, 0xd9, - 0xc4, 0xae, 0x60, 0xa1, 0x27, 0x3b, 0xbd, 0x7a, 0xd0, 0x41, 0x10, 0xbc, 0x2c, 0x99, 0x6c, 0x9c, - 0x09, 0x66, 0xf2, 0x86, 0x49, 0x66, 0xa0, 0xbd, 0xfa, 0x07, 0xfc, 0x1d, 0x9e, 0xfd, 0x11, 0x3d, - 0x16, 0x4f, 0x9e, 0x56, 0xd9, 0xfd, 0x07, 0x3d, 0x7b, 0x90, 0x49, 0x22, 0x4c, 0x29, 0x78, 0x9a, - 0xc9, 0x7b, 0xdf, 0xf7, 0xe5, 0x7d, 0xdf, 0x0b, 0x9e, 0xcb, 0x9c, 0x53, 0x56, 0xd7, 0x4a, 0x72, - 0x66, 0x25, 0x68, 0x43, 0x6d, 0xc3, 0xb4, 0xf9, 0x24, 0x1a, 0xda, 0x2d, 0x29, 0x6b, 0x6d, 0x79, - 0x45, 0xea, 0x06, 0x2c, 0x44, 0xcf, 0x65, 0xce, 0xc9, 0x10, 0x49, 0xfe, 0x21, 0x49, 0xb7, 0x3c, - 0x9a, 0x72, 0x30, 0x15, 0x98, 0x95, 0xc3, 0x52, 0x7f, 0xf0, 0xc4, 0xa3, 0xa7, 0x05, 0x14, 0xe0, - 0xeb, 0xfd, 0x5f, 0xa8, 0xc6, 0x1e, 0x43, 0x73, 0x66, 0x04, 0xed, 0x4e, 0x72, 0x61, 0xd9, 0x09, - 0xe5, 0x20, 0xb5, 0xef, 0x27, 0x7f, 0x10, 0x3e, 0x78, 0x0b, 0x8d, 0xbd, 0x28, 0x99, 0xd6, 0x42, - 0x9d, 0x57, 0xd0, 0x6a, 0x1b, 0x9d, 0xe2, 0xb1, 0x81, 0xb6, 0xe1, 0x62, 0x55, 0x43, 0x63, 0x27, - 0xe8, 0x18, 0xcd, 0x1f, 0xa7, 0xcf, 0x6e, 0x37, 0xb3, 0xe8, 0x92, 0x55, 0xea, 0x2c, 0x19, 0x34, - 0x93, 0x0c, 0xfb, 0x53, 0xaf, 0x12, 0xbd, 0xc6, 0x4f, 0x42, 0x8f, 0x7b, 0xc1, 0xc9, 0x03, 0xc7, - 0x9d, 0xde, 0x6e, 0x66, 0x87, 0x77, 0xb8, 0xa1, 0x9f, 0x64, 0xfb, 0xbe, 0x10, 0x06, 0x88, 0x14, - 0x1e, 0x9b, 0x5a, 0xe8, 0xf5, 0x4a, 0xc9, 0x4a, 0xda, 0xc9, 0xde, 0xf1, 0xde, 0x7c, 0xbc, 0x9c, - 0x92, 0x60, 0xb5, 0xb7, 0x41, 0x82, 0x0d, 0x72, 0x01, 0x52, 0xa7, 0x2f, 0xae, 0x37, 0xb3, 0xd1, - 0xb7, 0x5f, 0xb3, 0x79, 0x21, 0x6d, 0xd9, 0xe6, 0x84, 0x43, 0x15, 0x72, 0x09, 0x9f, 0x85, 0x59, - 0x7f, 0xa6, 0xf6, 0xb2, 0x16, 0xc6, 0x11, 0x4c, 0x86, 0x9d, 0xfe, 0x9b, 0x5e, 0x3e, 0xf9, 0x82, - 0xf0, 0xe1, 0xfb, 0x90, 0xef, 0x79, 0x6b, 0x4b, 0x68, 0xe4, 0x95, 0x8b, 0x3d, 0xfa, 0x80, 0xc7, - 0x4c, 0x29, 0x08, 0x4b, 0x98, 0x20, 0x37, 0x07, 0x25, 0xff, 0xdb, 0x0e, 0xb9, 0x17, 0x64, 0xfa, - 0xb0, 0x9f, 0x2e, 0x1b, 0x2a, 0x9d, 0x1d, 0xfc, 0xf8, 0xbe, 0xd8, 0xbf, 0x73, 0x57, 0xfa, 0xee, - 0x7a, 0x1b, 0xa3, 0x9b, 0x6d, 0x8c, 0x7e, 0x6f, 0x63, 0xf4, 0x75, 0x17, 0x8f, 0x6e, 0x76, 0xf1, - 0xe8, 0xe7, 0x2e, 0x1e, 0x7d, 0x3c, 0xbd, 0xef, 0x4a, 0xe6, 0x7c, 0x51, 0x00, 0xed, 0x5e, 0xd1, - 0x0a, 0xd6, 0xad, 0x12, 0xa6, 0x7f, 0x57, 0x83, 0xf7, 0xe4, 0xac, 0xe6, 0x8f, 0xdc, 0x7a, 0x5f, - 0xfe, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x2f, 0x11, 0x7e, 0x74, 0x79, 0x02, 0x00, 0x00, + // 442 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x52, 0xc1, 0x6a, 0xdb, 0x30, + 0x18, 0x8e, 0x9b, 0x32, 0xa8, 0x42, 0xc7, 0x62, 0xd6, 0xe1, 0x94, 0xe1, 0x04, 0x9f, 0x0c, 0x23, + 0xd2, 0x9a, 0xc1, 0x0a, 0x3d, 0x2d, 0xe9, 0x75, 0x87, 0xcd, 0x0c, 0x06, 0xbb, 0x04, 0x59, 0xd6, + 0x12, 0x31, 0x5b, 0xbf, 0x91, 0xe4, 0x8c, 0xf6, 0xba, 0x17, 0xd8, 0x6b, 0x6c, 0xe7, 0x3d, 0x44, + 0x8f, 0x65, 0xa7, 0x9d, 0xb2, 0x91, 0xbc, 0x41, 0x9f, 0x60, 0x58, 0x52, 0x21, 0xa5, 0xd0, 0x93, + 0xad, 0xff, 0xfb, 0xfe, 0x4f, 0xfa, 0xbf, 0xef, 0x47, 0xa9, 0xc8, 0x19, 0xa1, 0x75, 0x5d, 0x0a, + 0x46, 0x8d, 0x00, 0xa9, 0x89, 0x51, 0x54, 0xea, 0xcf, 0x5c, 0x91, 0xd5, 0x84, 0xd0, 0xc6, 0x2c, + 0x2f, 0x71, 0xad, 0xc0, 0x40, 0xf8, 0x5c, 0xe4, 0x0c, 0xef, 0x32, 0xf1, 0x2d, 0x13, 0xaf, 0x26, + 0xc7, 0x03, 0x06, 0xba, 0x02, 0x3d, 0xb7, 0x5c, 0xe2, 0x0e, 0xae, 0xf1, 0xf8, 0xe9, 0x02, 0x16, + 0xe0, 0xea, 0xed, 0x9f, 0xaf, 0xc6, 0x8e, 0x43, 0x72, 0xaa, 0x39, 0x59, 0x9d, 0xe4, 0xdc, 0xd0, + 0x13, 0xc2, 0x40, 0x48, 0x87, 0x27, 0x3f, 0xf6, 0x50, 0xff, 0x1d, 0x28, 0x73, 0xbe, 0xa4, 0x52, + 0xf2, 0x72, 0x5a, 0x41, 0x23, 0x4d, 0x78, 0x8a, 0x7a, 0x1a, 0x1a, 0xc5, 0xf8, 0xbc, 0x06, 0x65, + 0xa2, 0x60, 0x14, 0xa4, 0x07, 0xb3, 0x67, 0x37, 0xeb, 0x61, 0x78, 0x41, 0xab, 0xf2, 0x2c, 0xd9, + 0x01, 0x93, 0x0c, 0xb9, 0x53, 0xab, 0x12, 0xbe, 0x41, 0x8f, 0x3d, 0xc6, 0x9c, 0x60, 0xb4, 0x67, + 0x7b, 0x07, 0x37, 0xeb, 0xe1, 0xd1, 0x9d, 0x5e, 0x8f, 0x27, 0xd9, 0xa1, 0x2b, 0xf8, 0x07, 0x84, + 0x25, 0xea, 0xe9, 0x9a, 0xcb, 0x62, 0x5e, 0x8a, 0x4a, 0x98, 0xa8, 0x3b, 0xea, 0xa6, 0xbd, 0xc9, + 0x00, 0xfb, 0x51, 0xdb, 0x31, 0xb0, 0x1f, 0x03, 0x9f, 0x83, 0x90, 0xb3, 0x97, 0x57, 0xeb, 0x61, + 0xe7, 0xe7, 0xdf, 0x61, 0xba, 0x10, 0x66, 0xd9, 0xe4, 0x98, 0x41, 0xe5, 0x7d, 0xf1, 0x9f, 0xb1, + 0x2e, 0xbe, 0x10, 0x73, 0x51, 0x73, 0x6d, 0x1b, 0x74, 0x86, 0xac, 0xfe, 0xdb, 0x56, 0x3e, 0x7c, + 0x81, 0xfa, 0xb4, 0x2c, 0xe1, 0x2b, 0x2f, 0xe6, 0xb4, 0x28, 0x14, 0xd7, 0x9a, 0xeb, 0x68, 0x7f, + 0xd4, 0x4d, 0x0f, 0xb2, 0x27, 0x1e, 0x98, 0xde, 0xd6, 0x93, 0x6f, 0x01, 0x3a, 0xfa, 0xe0, 0xc3, + 0x98, 0x36, 0x66, 0x09, 0x4a, 0x5c, 0xda, 0x8c, 0xc2, 0x8f, 0xa8, 0xd7, 0xb2, 0x7d, 0x62, 0x51, + 0x60, 0x1f, 0x4d, 0xf0, 0x43, 0x51, 0xe2, 0x7b, 0xae, 0xcf, 0xf6, 0xdb, 0x51, 0xb2, 0x5d, 0xa5, + 0xb3, 0xfe, 0xef, 0x5f, 0xe3, 0xc3, 0x3b, 0x77, 0xcd, 0xde, 0x5f, 0x6d, 0xe2, 0xe0, 0x7a, 0x13, + 0x07, 0xff, 0x36, 0x71, 0xf0, 0x7d, 0x1b, 0x77, 0xae, 0xb7, 0x71, 0xe7, 0xcf, 0x36, 0xee, 0x7c, + 0x3a, 0xbd, 0x6f, 0x81, 0xc8, 0xd9, 0x78, 0x01, 0x64, 0xf5, 0x9a, 0x54, 0x50, 0x34, 0x25, 0xd7, + 0xed, 0x12, 0xee, 0x2c, 0x9f, 0xf5, 0x25, 0x7f, 0x64, 0x77, 0xe1, 0xd5, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x1b, 0x30, 0xdf, 0x96, 0xa6, 0x02, 0x00, 0x00, } func (m *PortChannelAmount) Marshal() (dAtA []byte, err error) { @@ -195,6 +206,15 @@ func (m *PortChannelAmount) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.AllowedAddresses) > 0 { + for iNdEx := len(m.AllowedAddresses) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.AllowedAddresses[iNdEx]) + copy(dAtA[i:], m.AllowedAddresses[iNdEx]) + i = encodeVarintAuthz(dAtA, i, uint64(len(m.AllowedAddresses[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } if len(m.SpendLimit) > 0 { for iNdEx := len(m.SpendLimit) - 1; iNdEx >= 0; iNdEx-- { { @@ -294,6 +314,12 @@ func (m *PortChannelAmount) Size() (n int) { n += 1 + l + sovAuthz(uint64(l)) } } + if len(m.AllowedAddresses) > 0 { + for _, s := range m.AllowedAddresses { + l = len(s) + n += 1 + l + sovAuthz(uint64(l)) + } + } return n } @@ -445,6 +471,38 @@ func (m *PortChannelAmount) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowedAddresses", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + 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 ErrInvalidLengthAuthz + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AllowedAddresses = append(m.AllowedAddresses, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipAuthz(dAtA[iNdEx:]) diff --git a/modules/apps/transfer/types/transfer_authz.go b/modules/apps/transfer/types/transfer_authz.go index d1c5d0e9fbb..11564491620 100644 --- a/modules/apps/transfer/types/transfer_authz.go +++ b/modules/apps/transfer/types/transfer_authz.go @@ -33,6 +33,15 @@ func (a TransferAuthorization) MsgTypeURL() string { return sdk.MsgTypeURL(&MsgTransfer{}) } +func IsAllowedAddress(receiver string, allowedAddrs []string) bool { + for _, addr := range allowedAddresses { + if addr == receiver { + return true + } + } + return false +} + // Accept implements Authorization.Accept. func (a TransferAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.AcceptResponse, error) { mTransfer, ok := msg.(*MsgTransfer) @@ -46,6 +55,11 @@ func (a TransferAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.Accep if isNegative { return authz.AcceptResponse{}, sdkerrors.ErrInsufficientFunds.Wrapf("requested amount is more than spend limit") } + + if !IsAllowedAddress(mTransfer.Receiver, allocation.AllowedAddresses) { + return authz.AcceptResponse{}, sdkerrors.ErrInsufficientFunds.Wrapf("not allowed address for transfer") + } + if limitLeft.IsZero() { a.Allocations = slices.Delete(a.Allocations, index, index+1) if len(a.Allocations) == 0 { diff --git a/proto/ibc/applications/transfer/v2/authz.proto b/proto/ibc/applications/transfer/v2/authz.proto index cba7ebed0c9..53ee25fc1cb 100644 --- a/proto/ibc/applications/transfer/v2/authz.proto +++ b/proto/ibc/applications/transfer/v2/authz.proto @@ -16,6 +16,8 @@ message PortChannelAmount { // spend limitation on the channel repeated cosmos.base.v1beta1.Coin spend_limit = 3 [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + // allowed addresses to be sent via transfer message + repeated string allowed_addresses = 4; } // TransferAuthorization allows the grantee to spend up to spend_limit coins from From 2b5ea1990ec0b0524153bbcf6511c102151f2b69 Mon Sep 17 00:00:00 2001 From: jgo121 Date: Mon, 3 Oct 2022 22:53:48 +0800 Subject: [PATCH 03/44] expect error when transferring to not allowed address --- modules/apps/transfer/types/transfer_authz.go | 18 ++++++++++-------- .../transfer/types/transfer_authz_test.go | 19 +++++++++++++------ 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/modules/apps/transfer/types/transfer_authz.go b/modules/apps/transfer/types/transfer_authz.go index 11564491620..ec6565a0ec7 100644 --- a/modules/apps/transfer/types/transfer_authz.go +++ b/modules/apps/transfer/types/transfer_authz.go @@ -14,13 +14,14 @@ var ( ) // NewTransferAuthorization creates a new TransferAuthorization object. -func NewTransferAuthorization(sourcePorts, sourceChannels []string, spendLimits []sdk.Coins) *TransferAuthorization { +func NewTransferAuthorization(sourcePorts, sourceChannels []string, spendLimits []sdk.Coins, allowedAddrs [][]string) *TransferAuthorization { allocations := []PortChannelAmount{} for index := range sourcePorts { allocations = append(allocations, PortChannelAmount{ - SourcePort: sourcePorts[index], - SourceChannel: sourceChannels[index], - SpendLimit: spendLimits[index], + SourcePort: sourcePorts[index], + SourceChannel: sourceChannels[index], + SpendLimit: spendLimits[index], + AllowedAddresses: allowedAddrs[index], }) } return &TransferAuthorization{ @@ -34,7 +35,7 @@ func (a TransferAuthorization) MsgTypeURL() string { } func IsAllowedAddress(receiver string, allowedAddrs []string) bool { - for _, addr := range allowedAddresses { + for _, addr := range allowedAddrs { if addr == receiver { return true } @@ -70,9 +71,10 @@ func (a TransferAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.Accep }}, nil } a.Allocations[index] = PortChannelAmount{ - SourcePort: allocation.SourcePort, - SourceChannel: allocation.SourceChannel, - SpendLimit: limitLeft, + SourcePort: allocation.SourcePort, + SourceChannel: allocation.SourceChannel, + SpendLimit: limitLeft, + AllowedAddresses: allocation.AllowedAddresses, } return authz.AcceptResponse{Accept: true, Delete: false, Updated: &TransferAuthorization{ diff --git a/modules/apps/transfer/types/transfer_authz_test.go b/modules/apps/transfer/types/transfer_authz_test.go index 838b26ee36c..022188e747c 100644 --- a/modules/apps/transfer/types/transfer_authz_test.go +++ b/modules/apps/transfer/types/transfer_authz_test.go @@ -26,7 +26,7 @@ var ( func TestTransferAuthorization(t *testing.T) { app := simapp.Setup(t, false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - authorization := NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins1000}) + authorization := NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins1000}, [][]string{{toAddr.String()}}) t.Log("verify authorization returns valid method name") require.Equal(t, authorization.MsgTypeURL(), "/ibc.applications.transfer.v1.MsgTransfer") @@ -41,7 +41,7 @@ func TestTransferAuthorization(t *testing.T) { require.Nil(t, resp.Updated) t.Log("verify updated authorization returns remaining spent limit") - authorization = NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins1000}) + authorization = NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins1000}, [][]string{{toAddr.String()}}) require.Equal(t, authorization.MsgTypeURL(), "/ibc.applications.transfer.v1.MsgTransfer") require.NoError(t, authorization.ValidateBasic()) transfer = NewMsgTransfer(sourcePort, sourceChannel, coin500, fromAddr.String(), toAddr.String(), timeoutHeight, 0) @@ -50,7 +50,7 @@ func TestTransferAuthorization(t *testing.T) { require.NoError(t, err) require.False(t, resp.Delete) require.NotNil(t, resp.Updated) - sendAuth := NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins500}) + sendAuth := NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins500}, [][]string{{toAddr.String()}}) require.Equal(t, sendAuth.String(), resp.Updated.String()) t.Log("expect updated authorization nil after spending remaining amount") @@ -60,7 +60,7 @@ func TestTransferAuthorization(t *testing.T) { require.Nil(t, resp.Updated) t.Log("expect error when spend limit for specific port and channel is not set") - authorization = NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins1000}) + authorization = NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins1000}, [][]string{{toAddr.String()}}) transfer = NewMsgTransfer(sourcePort2, sourceChannel2, coin500, fromAddr.String(), toAddr.String(), timeoutHeight, 0) _, err = authorization.Accept(ctx, transfer) require.Error(t, err) @@ -69,11 +69,18 @@ func TestTransferAuthorization(t *testing.T) { authorization = NewTransferAuthorization( []string{sourcePort, sourcePort2}, []string{sourceChannel, sourceChannel2}, - []sdk.Coins{coins1000, coins1000}) + []sdk.Coins{coins1000, coins1000}, + [][]string{{toAddr.String()}, {toAddr.String()}}) transfer = NewMsgTransfer(sourcePort, sourceChannel, coin1000, fromAddr.String(), toAddr.String(), timeoutHeight, 0) resp, err = authorization.Accept(ctx, transfer) require.NoError(t, err) require.NotNil(t, resp.Updated) - require.Equal(t, resp.Updated, NewTransferAuthorization([]string{sourcePort2}, []string{sourceChannel2}, []sdk.Coins{coins1000})) + require.Equal(t, resp.Updated, NewTransferAuthorization([]string{sourcePort2}, []string{sourceChannel2}, []sdk.Coins{coins1000}, [][]string{{toAddr.String()}})) require.False(t, resp.Delete) + + t.Log("expect error when transferring to not allowed address") + authorization = NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins1000}, [][]string{{fromAddr.String()}}) + transfer = NewMsgTransfer(sourcePort, sourceChannel, coin500, fromAddr.String(), toAddr.String(), timeoutHeight, 0) + _, err = authorization.Accept(ctx, transfer) + require.Error(t, err) } From 805afdf1cb64426bb71c6998e4f192ce573896c0 Mon Sep 17 00:00:00 2001 From: jgo121 Date: Tue, 27 Sep 2022 09:14:23 +0800 Subject: [PATCH 04/44] feat: authz grants for ics-20 transfers Create an authz grant that resricts to transfers to specific addresses and channels. --- docs/ibc/proto-docs.md | 54 ++ modules/apps/transfer/types/authz.pb.go | 694 ++++++++++++++++++ modules/apps/transfer/types/codec.go | 6 + modules/apps/transfer/types/transfer_authz.go | 105 +++ .../transfer/types/transfer_authz_test.go | 86 +++ .../ibc/applications/transfer/v2/authz.proto | 30 + 6 files changed, 975 insertions(+) create mode 100644 modules/apps/transfer/types/authz.pb.go create mode 100644 modules/apps/transfer/types/transfer_authz.go create mode 100644 modules/apps/transfer/types/transfer_authz_test.go create mode 100644 proto/ibc/applications/transfer/v2/authz.proto diff --git a/docs/ibc/proto-docs.md b/docs/ibc/proto-docs.md index 8183045f4fc..00b09cad82f 100644 --- a/docs/ibc/proto-docs.md +++ b/docs/ibc/proto-docs.md @@ -154,6 +154,10 @@ - [Msg](#ibc.applications.transfer.v1.Msg) +- [ibc/applications/transfer/v2/authz.proto](#ibc/applications/transfer/v2/authz.proto) + - [PortChannelAmount](#ibc.applications.transfer.v2.PortChannelAmount) + - [TransferAuthorization](#ibc.applications.transfer.v2.TransferAuthorization) + - [ibc/applications/transfer/v2/packet.proto](#ibc/applications/transfer/v2/packet.proto) - [FungibleTokenPacketData](#ibc.applications.transfer.v2.FungibleTokenPacketData) @@ -2291,6 +2295,56 @@ Msg defines the ibc/transfer Msg service. + +

Top

+ +## ibc/applications/transfer/v2/authz.proto + + + + + +### PortChannelAmount + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `source_port` | [string](#string) | | the port on which the packet will be sent | +| `source_channel` | [string](#string) | | the channel by which the packet will be sent | +| `spend_limit` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | spend limitation on the channel | +| `allowed_addresses` | [string](#string) | repeated | | + + + + + + + + +### TransferAuthorization +TransferAuthorization allows the grantee to spend up to spend_limit coins from +the granter's account for ibc transfer on a specific channel + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `allocations` | [PortChannelAmount](#ibc.applications.transfer.v2.PortChannelAmount) | repeated | port and channel amounts | + + + + + + + + + + + + + + +

Top

diff --git a/modules/apps/transfer/types/authz.pb.go b/modules/apps/transfer/types/authz.pb.go new file mode 100644 index 00000000000..db9a9e3dbc3 --- /dev/null +++ b/modules/apps/transfer/types/authz.pb.go @@ -0,0 +1,694 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/applications/transfer/v2/authz.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + _ "github.com/regen-network/cosmos-proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type PortChannelAmount struct { + // the port on which the packet will be sent + SourcePort string `protobuf:"bytes,1,opt,name=source_port,json=sourcePort,proto3" json:"source_port,omitempty" yaml:"source_port"` + // the channel by which the packet will be sent + SourceChannel string `protobuf:"bytes,2,opt,name=source_channel,json=sourceChannel,proto3" json:"source_channel,omitempty" yaml:"source_channel"` + // spend limitation on the channel + SpendLimit github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=spend_limit,json=spendLimit,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"spend_limit"` + // allowed addresses to be sent via transfer message + AllowedAddresses []string `protobuf:"bytes,4,rep,name=allowed_addresses,json=allowedAddresses,proto3" json:"allowed_addresses,omitempty"` +} + +func (m *PortChannelAmount) Reset() { *m = PortChannelAmount{} } +func (m *PortChannelAmount) String() string { return proto.CompactTextString(m) } +func (*PortChannelAmount) ProtoMessage() {} +func (*PortChannelAmount) Descriptor() ([]byte, []int) { + return fileDescriptor_c4c17169771443ae, []int{0} +} +func (m *PortChannelAmount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PortChannelAmount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PortChannelAmount.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 *PortChannelAmount) XXX_Merge(src proto.Message) { + xxx_messageInfo_PortChannelAmount.Merge(m, src) +} +func (m *PortChannelAmount) XXX_Size() int { + return m.Size() +} +func (m *PortChannelAmount) XXX_DiscardUnknown() { + xxx_messageInfo_PortChannelAmount.DiscardUnknown(m) +} + +var xxx_messageInfo_PortChannelAmount proto.InternalMessageInfo + +func (m *PortChannelAmount) GetSourcePort() string { + if m != nil { + return m.SourcePort + } + return "" +} + +func (m *PortChannelAmount) GetSourceChannel() string { + if m != nil { + return m.SourceChannel + } + return "" +} + +func (m *PortChannelAmount) GetSpendLimit() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.SpendLimit + } + return nil +} + +func (m *PortChannelAmount) GetAllowedAddresses() []string { + if m != nil { + return m.AllowedAddresses + } + return nil +} + +// TransferAuthorization allows the grantee to spend up to spend_limit coins from +// the granter's account for ibc transfer on a specific channel +type TransferAuthorization struct { + // port and channel amounts + Allocations []PortChannelAmount `protobuf:"bytes,1,rep,name=allocations,proto3" json:"allocations"` +} + +func (m *TransferAuthorization) Reset() { *m = TransferAuthorization{} } +func (m *TransferAuthorization) String() string { return proto.CompactTextString(m) } +func (*TransferAuthorization) ProtoMessage() {} +func (*TransferAuthorization) Descriptor() ([]byte, []int) { + return fileDescriptor_c4c17169771443ae, []int{1} +} +func (m *TransferAuthorization) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TransferAuthorization) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TransferAuthorization.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 *TransferAuthorization) XXX_Merge(src proto.Message) { + xxx_messageInfo_TransferAuthorization.Merge(m, src) +} +func (m *TransferAuthorization) XXX_Size() int { + return m.Size() +} +func (m *TransferAuthorization) XXX_DiscardUnknown() { + xxx_messageInfo_TransferAuthorization.DiscardUnknown(m) +} + +var xxx_messageInfo_TransferAuthorization proto.InternalMessageInfo + +func (m *TransferAuthorization) GetAllocations() []PortChannelAmount { + if m != nil { + return m.Allocations + } + return nil +} + +func init() { + proto.RegisterType((*PortChannelAmount)(nil), "ibc.applications.transfer.v2.PortChannelAmount") + proto.RegisterType((*TransferAuthorization)(nil), "ibc.applications.transfer.v2.TransferAuthorization") +} + +func init() { + proto.RegisterFile("ibc/applications/transfer/v2/authz.proto", fileDescriptor_c4c17169771443ae) +} + +var fileDescriptor_c4c17169771443ae = []byte{ + // 442 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x52, 0xc1, 0x6a, 0xdb, 0x30, + 0x18, 0x8e, 0x9b, 0x32, 0xa8, 0x42, 0xc7, 0x62, 0xd6, 0xe1, 0x94, 0xe1, 0x04, 0x9f, 0x0c, 0x23, + 0xd2, 0x9a, 0xc1, 0x0a, 0x3d, 0x2d, 0xe9, 0x75, 0x87, 0xcd, 0x0c, 0x06, 0xbb, 0x04, 0x59, 0xd6, + 0x12, 0x31, 0x5b, 0xbf, 0x91, 0xe4, 0x8c, 0xf6, 0xba, 0x17, 0xd8, 0x6b, 0x6c, 0xe7, 0x3d, 0x44, + 0x8f, 0x65, 0xa7, 0x9d, 0xb2, 0x91, 0xbc, 0x41, 0x9f, 0x60, 0x58, 0x52, 0x21, 0xa5, 0xd0, 0x93, + 0xad, 0xff, 0xfb, 0xfe, 0x4f, 0xfa, 0xbf, 0xef, 0x47, 0xa9, 0xc8, 0x19, 0xa1, 0x75, 0x5d, 0x0a, + 0x46, 0x8d, 0x00, 0xa9, 0x89, 0x51, 0x54, 0xea, 0xcf, 0x5c, 0x91, 0xd5, 0x84, 0xd0, 0xc6, 0x2c, + 0x2f, 0x71, 0xad, 0xc0, 0x40, 0xf8, 0x5c, 0xe4, 0x0c, 0xef, 0x32, 0xf1, 0x2d, 0x13, 0xaf, 0x26, + 0xc7, 0x03, 0x06, 0xba, 0x02, 0x3d, 0xb7, 0x5c, 0xe2, 0x0e, 0xae, 0xf1, 0xf8, 0xe9, 0x02, 0x16, + 0xe0, 0xea, 0xed, 0x9f, 0xaf, 0xc6, 0x8e, 0x43, 0x72, 0xaa, 0x39, 0x59, 0x9d, 0xe4, 0xdc, 0xd0, + 0x13, 0xc2, 0x40, 0x48, 0x87, 0x27, 0x3f, 0xf6, 0x50, 0xff, 0x1d, 0x28, 0x73, 0xbe, 0xa4, 0x52, + 0xf2, 0x72, 0x5a, 0x41, 0x23, 0x4d, 0x78, 0x8a, 0x7a, 0x1a, 0x1a, 0xc5, 0xf8, 0xbc, 0x06, 0x65, + 0xa2, 0x60, 0x14, 0xa4, 0x07, 0xb3, 0x67, 0x37, 0xeb, 0x61, 0x78, 0x41, 0xab, 0xf2, 0x2c, 0xd9, + 0x01, 0x93, 0x0c, 0xb9, 0x53, 0xab, 0x12, 0xbe, 0x41, 0x8f, 0x3d, 0xc6, 0x9c, 0x60, 0xb4, 0x67, + 0x7b, 0x07, 0x37, 0xeb, 0xe1, 0xd1, 0x9d, 0x5e, 0x8f, 0x27, 0xd9, 0xa1, 0x2b, 0xf8, 0x07, 0x84, + 0x25, 0xea, 0xe9, 0x9a, 0xcb, 0x62, 0x5e, 0x8a, 0x4a, 0x98, 0xa8, 0x3b, 0xea, 0xa6, 0xbd, 0xc9, + 0x00, 0xfb, 0x51, 0xdb, 0x31, 0xb0, 0x1f, 0x03, 0x9f, 0x83, 0x90, 0xb3, 0x97, 0x57, 0xeb, 0x61, + 0xe7, 0xe7, 0xdf, 0x61, 0xba, 0x10, 0x66, 0xd9, 0xe4, 0x98, 0x41, 0xe5, 0x7d, 0xf1, 0x9f, 0xb1, + 0x2e, 0xbe, 0x10, 0x73, 0x51, 0x73, 0x6d, 0x1b, 0x74, 0x86, 0xac, 0xfe, 0xdb, 0x56, 0x3e, 0x7c, + 0x81, 0xfa, 0xb4, 0x2c, 0xe1, 0x2b, 0x2f, 0xe6, 0xb4, 0x28, 0x14, 0xd7, 0x9a, 0xeb, 0x68, 0x7f, + 0xd4, 0x4d, 0x0f, 0xb2, 0x27, 0x1e, 0x98, 0xde, 0xd6, 0x93, 0x6f, 0x01, 0x3a, 0xfa, 0xe0, 0xc3, + 0x98, 0x36, 0x66, 0x09, 0x4a, 0x5c, 0xda, 0x8c, 0xc2, 0x8f, 0xa8, 0xd7, 0xb2, 0x7d, 0x62, 0x51, + 0x60, 0x1f, 0x4d, 0xf0, 0x43, 0x51, 0xe2, 0x7b, 0xae, 0xcf, 0xf6, 0xdb, 0x51, 0xb2, 0x5d, 0xa5, + 0xb3, 0xfe, 0xef, 0x5f, 0xe3, 0xc3, 0x3b, 0x77, 0xcd, 0xde, 0x5f, 0x6d, 0xe2, 0xe0, 0x7a, 0x13, + 0x07, 0xff, 0x36, 0x71, 0xf0, 0x7d, 0x1b, 0x77, 0xae, 0xb7, 0x71, 0xe7, 0xcf, 0x36, 0xee, 0x7c, + 0x3a, 0xbd, 0x6f, 0x81, 0xc8, 0xd9, 0x78, 0x01, 0x64, 0xf5, 0x9a, 0x54, 0x50, 0x34, 0x25, 0xd7, + 0xed, 0x12, 0xee, 0x2c, 0x9f, 0xf5, 0x25, 0x7f, 0x64, 0x77, 0xe1, 0xd5, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x1b, 0x30, 0xdf, 0x96, 0xa6, 0x02, 0x00, 0x00, +} + +func (m *PortChannelAmount) 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 *PortChannelAmount) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PortChannelAmount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AllowedAddresses) > 0 { + for iNdEx := len(m.AllowedAddresses) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.AllowedAddresses[iNdEx]) + copy(dAtA[i:], m.AllowedAddresses[iNdEx]) + i = encodeVarintAuthz(dAtA, i, uint64(len(m.AllowedAddresses[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.SpendLimit) > 0 { + for iNdEx := len(m.SpendLimit) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.SpendLimit[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAuthz(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.SourceChannel) > 0 { + i -= len(m.SourceChannel) + copy(dAtA[i:], m.SourceChannel) + i = encodeVarintAuthz(dAtA, i, uint64(len(m.SourceChannel))) + i-- + dAtA[i] = 0x12 + } + if len(m.SourcePort) > 0 { + i -= len(m.SourcePort) + copy(dAtA[i:], m.SourcePort) + i = encodeVarintAuthz(dAtA, i, uint64(len(m.SourcePort))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TransferAuthorization) 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 *TransferAuthorization) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TransferAuthorization) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Allocations) > 0 { + for iNdEx := len(m.Allocations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Allocations[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAuthz(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintAuthz(dAtA []byte, offset int, v uint64) int { + offset -= sovAuthz(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *PortChannelAmount) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SourcePort) + if l > 0 { + n += 1 + l + sovAuthz(uint64(l)) + } + l = len(m.SourceChannel) + if l > 0 { + n += 1 + l + sovAuthz(uint64(l)) + } + if len(m.SpendLimit) > 0 { + for _, e := range m.SpendLimit { + l = e.Size() + n += 1 + l + sovAuthz(uint64(l)) + } + } + if len(m.AllowedAddresses) > 0 { + for _, s := range m.AllowedAddresses { + l = len(s) + n += 1 + l + sovAuthz(uint64(l)) + } + } + return n +} + +func (m *TransferAuthorization) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Allocations) > 0 { + for _, e := range m.Allocations { + l = e.Size() + n += 1 + l + sovAuthz(uint64(l)) + } + } + return n +} + +func sovAuthz(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAuthz(x uint64) (n int) { + return sovAuthz(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *PortChannelAmount) 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 ErrIntOverflowAuthz + } + 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: PortChannelAmount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PortChannelAmount: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourcePort", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + 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 ErrInvalidLengthAuthz + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SourcePort = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourceChannel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + 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 ErrInvalidLengthAuthz + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SourceChannel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SpendLimit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAuthz + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SpendLimit = append(m.SpendLimit, types.Coin{}) + if err := m.SpendLimit[len(m.SpendLimit)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowedAddresses", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + 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 ErrInvalidLengthAuthz + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AllowedAddresses = append(m.AllowedAddresses, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuthz(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAuthz + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TransferAuthorization) 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 ErrIntOverflowAuthz + } + 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: TransferAuthorization: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TransferAuthorization: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Allocations", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAuthz + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Allocations = append(m.Allocations, PortChannelAmount{}) + if err := m.Allocations[len(m.Allocations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuthz(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAuthz + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAuthz(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthAuthz + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAuthz + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAuthz + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAuthz = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAuthz = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAuthz = fmt.Errorf("proto: unexpected end of group") +) diff --git a/modules/apps/transfer/types/codec.go b/modules/apps/transfer/types/codec.go index 24ad7e5a902..e8bac8f1db2 100644 --- a/modules/apps/transfer/types/codec.go +++ b/modules/apps/transfer/types/codec.go @@ -5,6 +5,7 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" + "github.com/cosmos/cosmos-sdk/x/authz" ) // RegisterLegacyAminoCodec registers the necessary x/ibc transfer interfaces and concrete types @@ -18,6 +19,11 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { func RegisterInterfaces(registry codectypes.InterfaceRegistry) { registry.RegisterImplementations((*sdk.Msg)(nil), &MsgTransfer{}) + registry.RegisterImplementations( + (*authz.Authorization)(nil), + &TransferAuthorization{}, + ) + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) } diff --git a/modules/apps/transfer/types/transfer_authz.go b/modules/apps/transfer/types/transfer_authz.go new file mode 100644 index 00000000000..ec6565a0ec7 --- /dev/null +++ b/modules/apps/transfer/types/transfer_authz.go @@ -0,0 +1,105 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/authz" + host "github.com/cosmos/ibc-go/v6/modules/core/24-host" + + "golang.org/x/exp/slices" +) + +var ( + _ authz.Authorization = &TransferAuthorization{} +) + +// NewTransferAuthorization creates a new TransferAuthorization object. +func NewTransferAuthorization(sourcePorts, sourceChannels []string, spendLimits []sdk.Coins, allowedAddrs [][]string) *TransferAuthorization { + allocations := []PortChannelAmount{} + for index := range sourcePorts { + allocations = append(allocations, PortChannelAmount{ + SourcePort: sourcePorts[index], + SourceChannel: sourceChannels[index], + SpendLimit: spendLimits[index], + AllowedAddresses: allowedAddrs[index], + }) + } + return &TransferAuthorization{ + Allocations: allocations, + } +} + +// MsgTypeURL implements Authorization.MsgTypeURL. +func (a TransferAuthorization) MsgTypeURL() string { + return sdk.MsgTypeURL(&MsgTransfer{}) +} + +func IsAllowedAddress(receiver string, allowedAddrs []string) bool { + for _, addr := range allowedAddrs { + if addr == receiver { + return true + } + } + return false +} + +// Accept implements Authorization.Accept. +func (a TransferAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.AcceptResponse, error) { + mTransfer, ok := msg.(*MsgTransfer) + if !ok { + return authz.AcceptResponse{}, sdkerrors.ErrInvalidType.Wrap("type mismatch") + } + + for index, allocation := range a.Allocations { + if allocation.SourceChannel == mTransfer.SourceChannel && allocation.SourcePort == mTransfer.SourcePort { + limitLeft, isNegative := allocation.SpendLimit.SafeSub(mTransfer.Token) + if isNegative { + return authz.AcceptResponse{}, sdkerrors.ErrInsufficientFunds.Wrapf("requested amount is more than spend limit") + } + + if !IsAllowedAddress(mTransfer.Receiver, allocation.AllowedAddresses) { + return authz.AcceptResponse{}, sdkerrors.ErrInsufficientFunds.Wrapf("not allowed address for transfer") + } + + if limitLeft.IsZero() { + a.Allocations = slices.Delete(a.Allocations, index, index+1) + if len(a.Allocations) == 0 { + return authz.AcceptResponse{Accept: true, Delete: true}, nil + } + return authz.AcceptResponse{Accept: true, Delete: false, Updated: &TransferAuthorization{ + Allocations: a.Allocations, + }}, nil + } + a.Allocations[index] = PortChannelAmount{ + SourcePort: allocation.SourcePort, + SourceChannel: allocation.SourceChannel, + SpendLimit: limitLeft, + AllowedAddresses: allocation.AllowedAddresses, + } + + return authz.AcceptResponse{Accept: true, Delete: false, Updated: &TransferAuthorization{ + Allocations: a.Allocations, + }}, nil + } + } + return authz.AcceptResponse{}, sdkerrors.ErrInsufficientFunds.Wrapf("requested port and channel allocation does not exist") +} + +// ValidateBasic implements Authorization.ValidateBasic. +func (a TransferAuthorization) ValidateBasic() error { + for _, allocation := range a.Allocations { + if allocation.SpendLimit == nil { + return sdkerrors.ErrInvalidCoins.Wrap("spend limit cannot be nil") + } + if !allocation.SpendLimit.IsAllPositive() { + return sdkerrors.ErrInvalidCoins.Wrapf("spend limit cannot be negitive") + } + if err := host.PortIdentifierValidator(allocation.SourcePort); err != nil { + return sdkerrors.Wrap(err, "invalid source port ID") + } + if err := host.ChannelIdentifierValidator(allocation.SourceChannel); err != nil { + return sdkerrors.Wrap(err, "invalid source channel ID") + } + } + return nil +} diff --git a/modules/apps/transfer/types/transfer_authz_test.go b/modules/apps/transfer/types/transfer_authz_test.go new file mode 100644 index 00000000000..022188e747c --- /dev/null +++ b/modules/apps/transfer/types/transfer_authz_test.go @@ -0,0 +1,86 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + sourcePort = "port" + sourceChannel = "channel-100" + sourcePort2 = "port2" + sourceChannel2 = "channel-101" + coins1000 = sdk.Coins{sdk.NewCoin("stake", sdk.NewInt(1000))} + coins500 = sdk.Coins{sdk.NewCoin("stake", sdk.NewInt(500))} + coin1000 = sdk.NewCoin("stake", sdk.NewInt(1000)) + coin500 = sdk.NewCoin("stake", sdk.NewInt(500)) + fromAddr = sdk.AccAddress("_____from _____") + toAddr = sdk.AccAddress("_______to________") +) + +func TestTransferAuthorization(t *testing.T) { + app := simapp.Setup(t, false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + authorization := NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins1000}, [][]string{{toAddr.String()}}) + + t.Log("verify authorization returns valid method name") + require.Equal(t, authorization.MsgTypeURL(), "/ibc.applications.transfer.v1.MsgTransfer") + require.NoError(t, authorization.ValidateBasic()) + transfer := NewMsgTransfer(sourcePort, sourceChannel, coin1000, fromAddr.String(), toAddr.String(), timeoutHeight, 0) + require.NoError(t, authorization.ValidateBasic()) + + t.Log("verify updated authorization returns nil") + resp, err := authorization.Accept(ctx, transfer) + require.NoError(t, err) + require.True(t, resp.Delete) + require.Nil(t, resp.Updated) + + t.Log("verify updated authorization returns remaining spent limit") + authorization = NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins1000}, [][]string{{toAddr.String()}}) + require.Equal(t, authorization.MsgTypeURL(), "/ibc.applications.transfer.v1.MsgTransfer") + require.NoError(t, authorization.ValidateBasic()) + transfer = NewMsgTransfer(sourcePort, sourceChannel, coin500, fromAddr.String(), toAddr.String(), timeoutHeight, 0) + require.NoError(t, authorization.ValidateBasic()) + resp, err = authorization.Accept(ctx, transfer) + require.NoError(t, err) + require.False(t, resp.Delete) + require.NotNil(t, resp.Updated) + sendAuth := NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins500}, [][]string{{toAddr.String()}}) + require.Equal(t, sendAuth.String(), resp.Updated.String()) + + t.Log("expect updated authorization nil after spending remaining amount") + resp, err = resp.Updated.Accept(ctx, transfer) + require.NoError(t, err) + require.True(t, resp.Delete) + require.Nil(t, resp.Updated) + + t.Log("expect error when spend limit for specific port and channel is not set") + authorization = NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins1000}, [][]string{{toAddr.String()}}) + transfer = NewMsgTransfer(sourcePort2, sourceChannel2, coin500, fromAddr.String(), toAddr.String(), timeoutHeight, 0) + _, err = authorization.Accept(ctx, transfer) + require.Error(t, err) + + t.Log("expect removing only 1 allocation if spend limit is finalized for the port") + authorization = NewTransferAuthorization( + []string{sourcePort, sourcePort2}, + []string{sourceChannel, sourceChannel2}, + []sdk.Coins{coins1000, coins1000}, + [][]string{{toAddr.String()}, {toAddr.String()}}) + transfer = NewMsgTransfer(sourcePort, sourceChannel, coin1000, fromAddr.String(), toAddr.String(), timeoutHeight, 0) + resp, err = authorization.Accept(ctx, transfer) + require.NoError(t, err) + require.NotNil(t, resp.Updated) + require.Equal(t, resp.Updated, NewTransferAuthorization([]string{sourcePort2}, []string{sourceChannel2}, []sdk.Coins{coins1000}, [][]string{{toAddr.String()}})) + require.False(t, resp.Delete) + + t.Log("expect error when transferring to not allowed address") + authorization = NewTransferAuthorization([]string{sourcePort}, []string{sourceChannel}, []sdk.Coins{coins1000}, [][]string{{fromAddr.String()}}) + transfer = NewMsgTransfer(sourcePort, sourceChannel, coin500, fromAddr.String(), toAddr.String(), timeoutHeight, 0) + _, err = authorization.Accept(ctx, transfer) + require.Error(t, err) +} diff --git a/proto/ibc/applications/transfer/v2/authz.proto b/proto/ibc/applications/transfer/v2/authz.proto new file mode 100644 index 00000000000..53ee25fc1cb --- /dev/null +++ b/proto/ibc/applications/transfer/v2/authz.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; + +package ibc.applications.transfer.v2; + +option go_package = "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"; + +import "cosmos_proto/cosmos.proto"; +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +message PortChannelAmount { + // the port on which the packet will be sent + string source_port = 1 [(gogoproto.moretags) = "yaml:\"source_port\""]; + // the channel by which the packet will be sent + string source_channel = 2 [(gogoproto.moretags) = "yaml:\"source_channel\""]; + // spend limitation on the channel + repeated cosmos.base.v1beta1.Coin spend_limit = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + // allowed addresses to be sent via transfer message + repeated string allowed_addresses = 4; +} + +// TransferAuthorization allows the grantee to spend up to spend_limit coins from +// the granter's account for ibc transfer on a specific channel +message TransferAuthorization { + option (cosmos_proto.implements_interface) = "Authorization"; + + // port and channel amounts + repeated PortChannelAmount allocations = 1 [(gogoproto.nullable) = false]; +} From 0bcb95e454111c8d2903267d85fe364bf863309c Mon Sep 17 00:00:00 2001 From: jgo121 Date: Mon, 5 Dec 2022 21:37:45 +0800 Subject: [PATCH 05/44] convert IsAllPositive to Validate --- modules/apps/transfer/types/transfer_authz.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/apps/transfer/types/transfer_authz.go b/modules/apps/transfer/types/transfer_authz.go index ec6565a0ec7..7e6aa4cbaff 100644 --- a/modules/apps/transfer/types/transfer_authz.go +++ b/modules/apps/transfer/types/transfer_authz.go @@ -91,8 +91,8 @@ func (a TransferAuthorization) ValidateBasic() error { if allocation.SpendLimit == nil { return sdkerrors.ErrInvalidCoins.Wrap("spend limit cannot be nil") } - if !allocation.SpendLimit.IsAllPositive() { - return sdkerrors.ErrInvalidCoins.Wrapf("spend limit cannot be negitive") + if err := allocation.SpendLimit.Validate(); err != nil { + return sdkerrors.ErrInvalidCoins.Wrapf(err.Error()) } if err := host.PortIdentifierValidator(allocation.SourcePort); err != nil { return sdkerrors.Wrap(err, "invalid source port ID") From 3290c1873f4568bb68207cae025035aad34eaae1 Mon Sep 17 00:00:00 2001 From: jgo121 Date: Mon, 5 Dec 2022 21:45:55 +0800 Subject: [PATCH 06/44] add gas cost per interation --- modules/apps/transfer/types/transfer_authz.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/apps/transfer/types/transfer_authz.go b/modules/apps/transfer/types/transfer_authz.go index 7e6aa4cbaff..64e2767445b 100644 --- a/modules/apps/transfer/types/transfer_authz.go +++ b/modules/apps/transfer/types/transfer_authz.go @@ -9,6 +9,8 @@ import ( "golang.org/x/exp/slices" ) +const gasCostPerIteration = uint64(10) + var ( _ authz.Authorization = &TransferAuthorization{} ) @@ -34,8 +36,9 @@ func (a TransferAuthorization) MsgTypeURL() string { return sdk.MsgTypeURL(&MsgTransfer{}) } -func IsAllowedAddress(receiver string, allowedAddrs []string) bool { +func IsAllowedAddress(ctx sdk.Context, receiver string, allowedAddrs []string) bool { for _, addr := range allowedAddrs { + ctx.GasMeter().ConsumeGas(gasCostPerIteration, "transfer authorization") if addr == receiver { return true } @@ -57,7 +60,7 @@ func (a TransferAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.Accep return authz.AcceptResponse{}, sdkerrors.ErrInsufficientFunds.Wrapf("requested amount is more than spend limit") } - if !IsAllowedAddress(mTransfer.Receiver, allocation.AllowedAddresses) { + if !IsAllowedAddress(ctx, mTransfer.Receiver, allocation.AllowedAddresses) { return authz.AcceptResponse{}, sdkerrors.ErrInsufficientFunds.Wrapf("not allowed address for transfer") } From 288f9c0945b468b9e0cdb46d1ef87dcfd231648d Mon Sep 17 00:00:00 2001 From: jgo121 Date: Mon, 5 Dec 2022 21:50:10 +0800 Subject: [PATCH 07/44] duplicated entry check --- modules/apps/transfer/types/errors.go | 1 + modules/apps/transfer/types/transfer_authz.go | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/modules/apps/transfer/types/errors.go b/modules/apps/transfer/types/errors.go index 0f0cb7c42a4..1dcbec14431 100644 --- a/modules/apps/transfer/types/errors.go +++ b/modules/apps/transfer/types/errors.go @@ -14,4 +14,5 @@ var ( ErrSendDisabled = sdkerrors.Register(ModuleName, 7, "fungible token transfers from this chain are disabled") ErrReceiveDisabled = sdkerrors.Register(ModuleName, 8, "fungible token transfers to this chain are disabled") ErrMaxTransferChannels = sdkerrors.Register(ModuleName, 9, "max transfer channels") + ErrDuplicateEntry = sdkerrors.Register(ModuleName, 10, "duplicated entry") ) diff --git a/modules/apps/transfer/types/transfer_authz.go b/modules/apps/transfer/types/transfer_authz.go index 64e2767445b..a9c861b1c51 100644 --- a/modules/apps/transfer/types/transfer_authz.go +++ b/modules/apps/transfer/types/transfer_authz.go @@ -103,6 +103,14 @@ func (a TransferAuthorization) ValidateBasic() error { if err := host.ChannelIdentifierValidator(allocation.SourceChannel); err != nil { return sdkerrors.Wrap(err, "invalid source channel ID") } + + found := make(map[string]bool, 0) + for i := 0; i < len(allocation.AllowedAddresses); i++ { + if found[allocation.AllowedAddresses[i]] { + return ErrDuplicateEntry + } + found[allocation.AllowedAddresses[i]] = true + } } return nil } From 1742c542913a94b5c702867d671f6ec1dee73c83 Mon Sep 17 00:00:00 2001 From: Charly Date: Mon, 5 Dec 2022 14:57:43 +0100 Subject: [PATCH 08/44] chore: scaffold custom IBC light client development guide (#2860) --- docs/.vuepress/config.js | 50 +++++++++++++++++++++++ docs/ibc/light-clients/client-state.md | 3 ++ docs/ibc/light-clients/consensus-state.md | 3 ++ docs/ibc/light-clients/genesis.md | 3 ++ docs/ibc/light-clients/misbehaviour.md | 3 ++ docs/ibc/light-clients/overview.md | 3 ++ docs/ibc/light-clients/proofs.md | 3 ++ docs/ibc/light-clients/proposal.md | 3 ++ docs/ibc/light-clients/update.md | 3 ++ docs/ibc/light-clients/upgrade.md | 3 ++ 10 files changed, 77 insertions(+) create mode 100644 docs/ibc/light-clients/client-state.md create mode 100644 docs/ibc/light-clients/consensus-state.md create mode 100644 docs/ibc/light-clients/genesis.md create mode 100644 docs/ibc/light-clients/misbehaviour.md create mode 100644 docs/ibc/light-clients/overview.md create mode 100644 docs/ibc/light-clients/proofs.md create mode 100644 docs/ibc/light-clients/proposal.md create mode 100644 docs/ibc/light-clients/update.md create mode 100644 docs/ibc/light-clients/upgrade.md diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 3c4ea705609..17eced0029d 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -294,6 +294,56 @@ module.exports = { }, ], }, + { + title: "IBC Light Client Developer Guide", + children: [ + { + title: "Overview", + directory: false, + path: "/light-clients/overview.html", + }, + { + title: "ClientState", + directory: false, + path: "light-clients/client-state.html", + }, + { + title: "ConsensusState", + directory: false, + path: "/light-clients/consensus-state.html", + }, + { + title: "Existence/Non-Existence Proofs", + directory: false, + path: "/light-clients/proofs.html", + }, + { + title: "Updates Handling", + directory: false, + path: "/light-clients/update.html", + }, + { + title: "Misbehaviour Handling", + directory: false, + path: "/light-clients/misbehaviour.html", + }, + { + title: "Upgrades Handling", + directory: false, + path: "/light-clients/upgrade.html", + }, + { + title: "Proposal Handling", + directory: false, + path: "/light-clients/proposal.html", + }, + { + title: "Genesis Handling", + directory: false, + path: "/light-clients/genesis.html", + }, + ], + }, { title: "IBC Middleware Modules", children: [ diff --git a/docs/ibc/light-clients/client-state.md b/docs/ibc/light-clients/client-state.md new file mode 100644 index 00000000000..cfb3805268f --- /dev/null +++ b/docs/ibc/light-clients/client-state.md @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/docs/ibc/light-clients/consensus-state.md b/docs/ibc/light-clients/consensus-state.md new file mode 100644 index 00000000000..04de84a83d6 --- /dev/null +++ b/docs/ibc/light-clients/consensus-state.md @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/docs/ibc/light-clients/genesis.md b/docs/ibc/light-clients/genesis.md new file mode 100644 index 00000000000..695231196ad --- /dev/null +++ b/docs/ibc/light-clients/genesis.md @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/docs/ibc/light-clients/misbehaviour.md b/docs/ibc/light-clients/misbehaviour.md new file mode 100644 index 00000000000..99529dc2775 --- /dev/null +++ b/docs/ibc/light-clients/misbehaviour.md @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/docs/ibc/light-clients/overview.md b/docs/ibc/light-clients/overview.md new file mode 100644 index 00000000000..4e10266dfdb --- /dev/null +++ b/docs/ibc/light-clients/overview.md @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/docs/ibc/light-clients/proofs.md b/docs/ibc/light-clients/proofs.md new file mode 100644 index 00000000000..197c44cbc2b --- /dev/null +++ b/docs/ibc/light-clients/proofs.md @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/docs/ibc/light-clients/proposal.md b/docs/ibc/light-clients/proposal.md new file mode 100644 index 00000000000..1aa80c4f77e --- /dev/null +++ b/docs/ibc/light-clients/proposal.md @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/docs/ibc/light-clients/update.md b/docs/ibc/light-clients/update.md new file mode 100644 index 00000000000..ff7a3c6ce6b --- /dev/null +++ b/docs/ibc/light-clients/update.md @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/docs/ibc/light-clients/upgrade.md b/docs/ibc/light-clients/upgrade.md new file mode 100644 index 00000000000..cf8eae46ed7 --- /dev/null +++ b/docs/ibc/light-clients/upgrade.md @@ -0,0 +1,3 @@ + \ No newline at end of file From 412e213a7a9dc03c3455f45771e4df08a9b14569 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Tue, 6 Dec 2022 09:24:43 +0000 Subject: [PATCH 09/44] chore: changing ibcprotocol.org to ibcprotocol.dev (#2884) --- README.md | 4 ++-- docs/.vuepress/config.js | 4 ++-- docs/ibc/apps/ibcmodule.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2bd1cbe9c99..e78c3043209 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ E2E Status -The [Inter-Blockchain Communication protocol (IBC)](https://ibcprotocol.org/) allows blockchains to talk to each other. IBC handles transport across different sovereign blockchains. This end-to-end, connection-oriented, stateful protocol provides reliable, ordered, and authenticated communication between heterogeneous blockchains. This IBC implementation in Golang is built as a Cosmos SDK module. +The [Inter-Blockchain Communication protocol (IBC)](https://ibcprotocol.dev/) allows blockchains to talk to each other. IBC handles transport across different sovereign blockchains. This end-to-end, connection-oriented, stateful protocol provides reliable, ordered, and authenticated communication between heterogeneous blockchains. This IBC implementation in Golang is built as a Cosmos SDK module. ## Roadmap @@ -120,6 +120,6 @@ To report a security vulnerability, see our [bug bounty program](https://hackero ## Documentation and Resources -- [IBC Website](https://ibcprotocol.org/) +- [IBC Website](https://ibcprotocol.dev/) - [IBC Specification](https://github.com/cosmos/ibc) - [Documentation](https://ibc.cosmos.network/main/ibc/overview.html) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 17eced0029d..d89efb89ac6 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -462,8 +462,8 @@ module.exports = { text: "Chat with IBC developers in Discord.", }, textLink: { - text: "ibcprotocol.org", - url: "https://ibcprotocol.org", + text: "ibcprotocol.dev", + url: "https://ibcprotocol.dev", }, services: [ { diff --git a/docs/ibc/apps/ibcmodule.md b/docs/ibc/apps/ibcmodule.md index 2a4a7473657..65dc28d4de5 100644 --- a/docs/ibc/apps/ibcmodule.md +++ b/docs/ibc/apps/ibcmodule.md @@ -209,7 +209,7 @@ Just as IBC expects modules to implement callbacks for channel handshakes, it al Once a module A and module B are connected to each other, relayers can start relaying packets and acknowledgements back and forth on the channel. -![IBC packet flow diagram](https://ibcprotocol.org/_nuxt/img/packet_flow.1d89ee0.png) +![IBC packet flow diagram](https://ibcprotocol.dev/_nuxt/img/packet_flow.1d89ee0.png) Briefly, a successful packet flow works as follows: From 72fe6934317d11fdc7bfc87893306e24eaa762b8 Mon Sep 17 00:00:00 2001 From: Charly Date: Tue, 6 Dec 2022 12:31:12 +0100 Subject: [PATCH 10/44] chore: update README to include link to IDA (#2887) * update README to reflect new website * pr comment --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e78c3043209..cc0f2915bc6 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,9 @@ E2E Status -The [Inter-Blockchain Communication protocol (IBC)](https://ibcprotocol.dev/) allows blockchains to talk to each other. IBC handles transport across different sovereign blockchains. This end-to-end, connection-oriented, stateful protocol provides reliable, ordered, and authenticated communication between heterogeneous blockchains. This IBC implementation in Golang is built as a Cosmos SDK module. +The [Inter-Blockchain Communication protocol (IBC)](https://ibcprotocol.dev/) allows blockchains to talk to each other. This end-to-end, connection-oriented, stateful protocol provides reliable, ordered, and authenticated communication between heterogeneous blockchains. + +This IBC implementation in Golang is built as a Cosmos SDK module. To understand more about how to use the `ibc-go` module as well as about the IBC protocol, please check out the Interchain Developer Academy [section on IBC](https://tutorials.cosmos.network/academy/3-ibc/), or [our docs](https://ibc.cosmos.network/main/ibc/overview.html). ## Roadmap From 85ebc84932226c35ba13603b5bfb1f8548ed6e29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Wed, 7 Dec 2022 12:23:01 +0100 Subject: [PATCH 11/44] feat: Add genesis migrations for v6 to v7. The migration migrates the solo machine client state definition, removes all solo machine consensus states and removes the localhost client. (#2824) --- CHANGELOG.md | 1 + .../core/02-client/migrations/v7/genesis.go | 77 ++++++++ .../02-client/migrations/v7/genesis_test.go | 134 +++++++++++++ modules/core/legacy/v100/genesis.go | 54 ------ modules/core/legacy/v100/genesis_test.go | 177 ------------------ modules/core/migrations/v7/genesis.go | 42 +++++ modules/core/migrations/v7/genesis_test.go | 160 ++++++++++++++++ modules/core/migrations/v7/migrations.go | 44 ----- modules/core/migrations/v7/migrations_test.go | 116 ------------ 9 files changed, 414 insertions(+), 391 deletions(-) create mode 100644 modules/core/02-client/migrations/v7/genesis.go create mode 100644 modules/core/02-client/migrations/v7/genesis_test.go delete mode 100644 modules/core/legacy/v100/genesis.go delete mode 100644 modules/core/legacy/v100/genesis_test.go create mode 100644 modules/core/migrations/v7/genesis.go create mode 100644 modules/core/migrations/v7/genesis_test.go delete mode 100644 modules/core/migrations/v7/migrations.go delete mode 100644 modules/core/migrations/v7/migrations_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d4002d44ec..a502dd7f70f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -117,6 +117,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* (core/02-client) [\#2824](https://github.com/cosmos/ibc-go/pull/2824) Add genesis migrations for v6 to v7. The migration migrates the solo machine client state definition, removes all solo machine consensus states and removes the localhost client. * (core/24-host) [\#2856](https://github.com/cosmos/ibc-go/pull/2856) Add `PrefixedClientStorePath` and `PrefixedClientStoreKey` functions to 24-host * (core/02-client) [\#2819](https://github.com/cosmos/ibc-go/pull/2819) Add automatic in-place store migrations to remove the localhost client and migrate existing solo machine definitions. * (light-clients/06-solomachine) [\#2826](https://github.com/cosmos/ibc-go/pull/2826) Add `AppModuleBasic` for the 06-solomachine client and remove solo machine type registration from core IBC. Chains must register the `AppModuleBasic` of light clients. diff --git a/modules/core/02-client/migrations/v7/genesis.go b/modules/core/02-client/migrations/v7/genesis.go new file mode 100644 index 00000000000..b1071f1a4f4 --- /dev/null +++ b/modules/core/02-client/migrations/v7/genesis.go @@ -0,0 +1,77 @@ +package v7 + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v6/modules/core/exported" +) + +// MigrateGenesis accepts an exported IBC client genesis file and migrates it to: +// +// - Update solo machine client state protobuf definition (v2 to v3) +// - Remove all solo machine consensus states +// - Remove localhost client +func MigrateGenesis(clientGenState *clienttypes.GenesisState, cdc codec.ProtoCodecMarshaler) (*clienttypes.GenesisState, error) { + // To prune the client and consensus states, we will create new slices to fill up + // with information we want to keep. + var ( + clientsConsensus []clienttypes.ClientConsensusStates + clients []clienttypes.IdentifiedClientState + ) + + for _, client := range clientGenState.Clients { + clientType, _, err := clienttypes.ParseClientIdentifier(client.ClientId) + if err != nil { + return nil, err + } + + switch clientType { + case exported.Solomachine: + var clientState ClientState + if err := cdc.Unmarshal(client.ClientState.Value, &clientState); err != nil { + return nil, sdkerrors.Wrap(err, "failed to unmarshal client state bytes into solo machine client state") + } + + updatedClientState := migrateSolomachine(clientState) + + protoAny, err := clienttypes.PackClientState(&updatedClientState) + if err != nil { + return nil, err + } + + clients = append(clients, clienttypes.IdentifiedClientState{ + ClientId: client.ClientId, + ClientState: protoAny, + }) + + case Localhost: + // remove localhost client state by not adding client state + + default: + // add all other client states + clients = append(clients, client) + } + + // iterate consensus states by client + for _, clientConsensusStates := range clientGenState.ClientsConsensus { + // look for consensus states for the current client + if clientConsensusStates.ClientId == client.ClientId { + switch clientType { + case exported.Solomachine, Localhost: + // remove all consensus states for the solo machine and localhost + // do not add to new clientsConsensus + + default: + // ensure all consensus states added for other client types + clientsConsensus = append(clientsConsensus, clientConsensusStates) + } + } + } + } + + clientGenState.Clients = clients + clientGenState.ClientsConsensus = clientsConsensus + return clientGenState, nil +} diff --git a/modules/core/02-client/migrations/v7/genesis_test.go b/modules/core/02-client/migrations/v7/genesis_test.go new file mode 100644 index 00000000000..990fc7d367e --- /dev/null +++ b/modules/core/02-client/migrations/v7/genesis_test.go @@ -0,0 +1,134 @@ +package v7_test + +import ( + "encoding/json" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + + ibcclient "github.com/cosmos/ibc-go/v6/modules/core/02-client" + "github.com/cosmos/ibc-go/v6/modules/core/02-client/migrations/v7" + "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v6/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v6/testing" +) + +func (suite *MigrationsV7TestSuite) TestMigrateGenesisSolomachine() { + // create tendermint clients + for i := 0; i < 3; i++ { + path := ibctesting.NewPath(suite.chainA, suite.chainB) + + suite.coordinator.SetupClients(path) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + // update a second time to add more state + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + } + + // create multiple legacy solo machine clients + solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1) + solomachineMulti := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4) + + clientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) + + // manually generate old proto buf definitions and set in genesis + // NOTE: we cannot use 'ExportGenesis' for the solo machines since we are + // using client states and consensus states which do not implement the exported.ClientState + // and exported.ConsensusState interface + var clients []types.IdentifiedClientState + for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} { + clientState := sm.ClientState() + + // generate old client state proto definition + legacyClientState := &v7.ClientState{ + Sequence: clientState.Sequence, + ConsensusState: &v7.ConsensusState{ + PublicKey: clientState.ConsensusState.PublicKey, + Diversifier: clientState.ConsensusState.Diversifier, + Timestamp: clientState.ConsensusState.Timestamp, + }, + AllowUpdateAfterProposal: true, + } + + // set client state + protoAny, err := codectypes.NewAnyWithValue(legacyClientState) + suite.Require().NoError(err) + suite.Require().NotNil(protoAny) + + clients = append(clients, types.IdentifiedClientState{ + ClientId: sm.ClientID, + ClientState: protoAny, + }) + + // set in store for ease of determining expected genesis + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), sm.ClientID) + bz, err := suite.chainA.App.AppCodec().MarshalInterface(legacyClientState) + suite.Require().NoError(err) + clientStore.Set(host.ClientStateKey(), bz) + + protoAny, err = codectypes.NewAnyWithValue(legacyClientState.ConsensusState) + suite.Require().NoError(err) + suite.Require().NotNil(protoAny) + + // obtain marshalled bytes to set in client store + bz, err = suite.chainA.App.AppCodec().MarshalInterface(legacyClientState.ConsensusState) + suite.Require().NoError(err) + + var consensusStates []types.ConsensusStateWithHeight + + // set consensus states in store and genesis + for i := uint64(0); i < numCreations; i++ { + height := types.NewHeight(1, i) + clientStore.Set(host.ConsensusStateKey(height), bz) + consensusStates = append(consensusStates, types.ConsensusStateWithHeight{ + Height: height, + ConsensusState: protoAny, + }) + } + + clientGenState.ClientsConsensus = append(clientGenState.ClientsConsensus, types.ClientConsensusStates{ + ClientId: sm.ClientID, + ConsensusStates: consensusStates, + }) + } + + // solo machine clients must come before tendermint in expected + clientGenState.Clients = append(clients, clientGenState.Clients...) + + // migrate store get expected genesis + // store migration and genesis migration should produce identical results + // NOTE: tendermint clients are not pruned in genesis so the test should not have expired tendermint clients + err := v7.MigrateStore(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetKey(host.StoreKey), suite.chainA.App.AppCodec()) + suite.Require().NoError(err) + expectedClientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) + + cdc, ok := suite.chainA.App.AppCodec().(codec.ProtoCodecMarshaler) + suite.Require().True(ok) + + migrated, err := v7.MigrateGenesis(&clientGenState, cdc) + suite.Require().NoError(err) + + bz, err := cdc.MarshalJSON(&expectedClientGenState) + suite.Require().NoError(err) + + // Indent the JSON bz correctly. + var jsonObj map[string]interface{} + err = json.Unmarshal(bz, &jsonObj) + suite.Require().NoError(err) + expectedIndentedBz, err := json.MarshalIndent(jsonObj, "", "\t") + suite.Require().NoError(err) + + bz, err = cdc.MarshalJSON(migrated) + suite.Require().NoError(err) + + // Indent the JSON bz correctly. + err = json.Unmarshal(bz, &jsonObj) + suite.Require().NoError(err) + indentedBz, err := json.MarshalIndent(jsonObj, "", "\t") + suite.Require().NoError(err) + + suite.Require().Equal(string(expectedIndentedBz), string(indentedBz)) +} diff --git a/modules/core/legacy/v100/genesis.go b/modules/core/legacy/v100/genesis.go deleted file mode 100644 index 668d8a1f78a..00000000000 --- a/modules/core/legacy/v100/genesis.go +++ /dev/null @@ -1,54 +0,0 @@ -package v100 - -import ( - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - tmtypes "github.com/tendermint/tendermint/types" - - clientv100 "github.com/cosmos/ibc-go/v6/modules/core/02-client/legacy/v100" - clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v6/modules/core/24-host" - "github.com/cosmos/ibc-go/v6/modules/core/types" -) - -// MigrateGenesis accepts exported v1.0.0 IBC client genesis file and migrates it to: -// -// - Update solo machine client state protobuf definition (v1 to v2) -// - Remove all solo machine consensus states -// - Remove all expired tendermint consensus states -func MigrateGenesis(appState genutiltypes.AppMap, clientCtx client.Context, genDoc tmtypes.GenesisDoc, maxExpectedTimePerBlock uint64) (genutiltypes.AppMap, error) { - if appState[host.ModuleName] != nil { - // ensure legacy solo machines are registered - clientv100.RegisterInterfaces(clientCtx.InterfaceRegistry) - - // unmarshal relative source genesis application state - ibcGenState := &types.GenesisState{} - clientCtx.Codec.MustUnmarshalJSON(appState[host.ModuleName], ibcGenState) - - clientGenState, err := clientv100.MigrateGenesis(codec.NewProtoCodec(clientCtx.InterfaceRegistry), &ibcGenState.ClientGenesis, genDoc.GenesisTime, clienttypes.NewHeight(clienttypes.ParseChainID(genDoc.ChainID), uint64(genDoc.InitialHeight))) - if err != nil { - return nil, err - } - - ibcGenState.ClientGenesis = *clientGenState - - // set max expected time per block - connectionGenesis := connectiontypes.GenesisState{ - Connections: ibcGenState.ConnectionGenesis.Connections, - ClientConnectionPaths: ibcGenState.ConnectionGenesis.ClientConnectionPaths, - NextConnectionSequence: ibcGenState.ConnectionGenesis.NextConnectionSequence, - Params: connectiontypes.NewParams(maxExpectedTimePerBlock), - } - - ibcGenState.ConnectionGenesis = connectionGenesis - - // delete old genesis state - delete(appState, host.ModuleName) - - // set new ibc genesis state - appState[host.ModuleName] = clientCtx.Codec.MustMarshalJSON(ibcGenState) - } - return appState, nil -} diff --git a/modules/core/legacy/v100/genesis_test.go b/modules/core/legacy/v100/genesis_test.go deleted file mode 100644 index c9f1e800ce0..00000000000 --- a/modules/core/legacy/v100/genesis_test.go +++ /dev/null @@ -1,177 +0,0 @@ -package v100_test - -import ( - "testing" - - "github.com/cosmos/cosmos-sdk/client" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - "github.com/stretchr/testify/suite" - tmtypes "github.com/tendermint/tendermint/types" - - ibcclient "github.com/cosmos/ibc-go/v6/modules/core/02-client" - clientv100 "github.com/cosmos/ibc-go/v6/modules/core/02-client/legacy/v100" - clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v6/modules/core/24-host" - v100 "github.com/cosmos/ibc-go/v6/modules/core/legacy/v100" - "github.com/cosmos/ibc-go/v6/modules/core/types" - ibctesting "github.com/cosmos/ibc-go/v6/testing" - "github.com/cosmos/ibc-go/v6/testing/simapp" -) - -type LegacyTestSuite struct { - suite.Suite - - coordinator *ibctesting.Coordinator - - // testing chains used for convenience and readability - chainA *ibctesting.TestChain - chainB *ibctesting.TestChain -} - -// TestLegacyTestSuite runs all the tests within this package. -func TestLegacyTestSuite(t *testing.T) { - suite.Run(t, new(LegacyTestSuite)) -} - -// SetupTest creates a coordinator with 2 test chains. -func (suite *LegacyTestSuite) SetupTest() { - suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) - suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) - suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) - // commit some blocks so that QueryProof returns valid proof (cannot return valid query if height <= 1) - suite.coordinator.CommitNBlocks(suite.chainA, 2) - suite.coordinator.CommitNBlocks(suite.chainB, 2) -} - -// NOTE: this test is mainly copied from 02-client/legacy/v100 -func (suite *LegacyTestSuite) TestMigrateGenesisSolomachine() { - path := ibctesting.NewPath(suite.chainA, suite.chainB) - encodingConfig := simapp.MakeTestEncodingConfig() - clientCtx := client.Context{}. - WithInterfaceRegistry(encodingConfig.InterfaceRegistry). - WithTxConfig(encodingConfig.TxConfig). - WithCodec(encodingConfig.Marshaler) - - // create multiple legacy solo machine clients - solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1) - solomachineMulti := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4) - - // create tendermint clients - // NOTE: only 1 set of metadata is created, we aren't testing ordering - // The purpose of this test is to ensure the genesis states can be marshalled/unmarshalled - suite.coordinator.SetupClients(path) - clientGenState := ibcclient.ExportGenesis(path.EndpointA.Chain.GetContext(), path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper) - - // manually generate old proto buf definitions and set in genesis - // NOTE: we cannot use 'ExportGenesis' for the solo machines since we are - // using client states and consensus states which do not implement the exported.ClientState - // and exported.ConsensusState interface - var clients []clienttypes.IdentifiedClientState - for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} { - clientState := sm.ClientState() - - var seq uint64 - if clientState.IsFrozen { - seq = 1 - } - - // generate old client state proto definition - legacyClientState := &clientv100.ClientState{ - Sequence: clientState.Sequence, - FrozenSequence: seq, - ConsensusState: &clientv100.ConsensusState{ - PublicKey: clientState.ConsensusState.PublicKey, - Diversifier: clientState.ConsensusState.Diversifier, - Timestamp: clientState.ConsensusState.Timestamp, - }, - } - - // set client state - any, err := codectypes.NewAnyWithValue(legacyClientState) - suite.Require().NoError(err) - suite.Require().NotNil(any) - client := clienttypes.IdentifiedClientState{ - ClientId: sm.ClientID, - ClientState: any, - } - clients = append(clients, client) - - // set in store for ease of determining expected genesis - clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(path.EndpointA.Chain.GetContext(), sm.ClientID) - bz, err := path.EndpointA.Chain.App.AppCodec().MarshalInterface(legacyClientState) - suite.Require().NoError(err) - clientStore.Set(host.ClientStateKey(), bz) - - // set some consensus states - height1 := clienttypes.NewHeight(0, 1) - height2 := clienttypes.NewHeight(1, 2) - height3 := clienttypes.NewHeight(0, 123) - - any, err = codectypes.NewAnyWithValue(legacyClientState.ConsensusState) - suite.Require().NoError(err) - suite.Require().NotNil(any) - consensusState1 := clienttypes.ConsensusStateWithHeight{ - Height: height1, - ConsensusState: any, - } - consensusState2 := clienttypes.ConsensusStateWithHeight{ - Height: height2, - ConsensusState: any, - } - consensusState3 := clienttypes.ConsensusStateWithHeight{ - Height: height3, - ConsensusState: any, - } - - clientConsensusState := clienttypes.ClientConsensusStates{ - ClientId: sm.ClientID, - ConsensusStates: []clienttypes.ConsensusStateWithHeight{consensusState1, consensusState2, consensusState3}, - } - - clientGenState.ClientsConsensus = append(clientGenState.ClientsConsensus, clientConsensusState) - - // set in store for ease of determining expected genesis - bz, err = path.EndpointA.Chain.App.AppCodec().MarshalInterface(legacyClientState.ConsensusState) - suite.Require().NoError(err) - clientStore.Set(host.ConsensusStateKey(height1), bz) - clientStore.Set(host.ConsensusStateKey(height2), bz) - clientStore.Set(host.ConsensusStateKey(height3), bz) - } - // solo machine clients must come before tendermint in expected - clientGenState.Clients = append(clients, clientGenState.Clients...) - - // migrate store get expected genesis - // store migration and genesis migration should produce identical results - err := clientv100.MigrateStore(path.EndpointA.Chain.GetContext(), path.EndpointA.Chain.GetSimApp().GetKey(host.StoreKey), path.EndpointA.Chain.App.AppCodec()) - suite.Require().NoError(err) - expectedClientGenState := ibcclient.ExportGenesis(path.EndpointA.Chain.GetContext(), path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper) - - // NOTE: these lines are added in comparison to 02-client/legacy/v100 - // generate appState with old ibc genesis state - appState := genutiltypes.AppMap{} - ibcGenState := types.DefaultGenesisState() - ibcGenState.ClientGenesis = clientGenState - clientv100.RegisterInterfaces(clientCtx.InterfaceRegistry) - appState[host.ModuleName] = clientCtx.Codec.MustMarshalJSON(ibcGenState) - genDoc := tmtypes.GenesisDoc{ - ChainID: suite.chainA.ChainID, - GenesisTime: suite.coordinator.CurrentTime, - InitialHeight: suite.chainA.GetContext().BlockHeight(), - } - - // NOTE: genesis time isn't updated since we aren't testing for tendermint consensus state pruning - migrated, err := v100.MigrateGenesis(appState, clientCtx, genDoc, uint64(connectiontypes.DefaultTimePerBlock)) - suite.Require().NoError(err) - - expectedAppState := genutiltypes.AppMap{} - expectedIBCGenState := types.DefaultGenesisState() - expectedIBCGenState.ClientGenesis = expectedClientGenState - - bz, err := clientCtx.Codec.MarshalJSON(expectedIBCGenState) - suite.Require().NoError(err) - expectedAppState[host.ModuleName] = bz - - suite.Require().Equal(expectedAppState, migrated) -} diff --git a/modules/core/migrations/v7/genesis.go b/modules/core/migrations/v7/genesis.go new file mode 100644 index 00000000000..356589fd3d3 --- /dev/null +++ b/modules/core/migrations/v7/genesis.go @@ -0,0 +1,42 @@ +package v7 + +import ( + "github.com/cosmos/cosmos-sdk/codec" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + + clientv7 "github.com/cosmos/ibc-go/v6/modules/core/02-client/migrations/v7" + host "github.com/cosmos/ibc-go/v6/modules/core/24-host" + "github.com/cosmos/ibc-go/v6/modules/core/types" +) + +// MigrateGenesis accepts an exported IBC client genesis file and migrates it to: +// +// - Update solo machine client state protobuf definition (v2 to v3) +// - Remove all solo machine consensus states +// - Remove any localhost clients +func MigrateGenesis(appState genutiltypes.AppMap, cdc codec.ProtoCodecMarshaler) (genutiltypes.AppMap, error) { + if appState[host.ModuleName] == nil { + return appState, nil + } + + // ensure legacy solo machines types are registered + clientv7.RegisterInterfaces(cdc.InterfaceRegistry()) + + // unmarshal old ibc genesis state + ibcGenState := &types.GenesisState{} + cdc.MustUnmarshalJSON(appState[host.ModuleName], ibcGenState) + + clientGenState, err := clientv7.MigrateGenesis(&ibcGenState.ClientGenesis, cdc) + if err != nil { + return nil, err + } + + ibcGenState.ClientGenesis = *clientGenState + + // delete old genesis state + delete(appState, host.ModuleName) + + // set new ibc genesis state + appState[host.ModuleName] = cdc.MustMarshalJSON(ibcGenState) + return appState, nil +} diff --git a/modules/core/migrations/v7/genesis_test.go b/modules/core/migrations/v7/genesis_test.go new file mode 100644 index 00000000000..1ba9fcdf48d --- /dev/null +++ b/modules/core/migrations/v7/genesis_test.go @@ -0,0 +1,160 @@ +package v7_test + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/stretchr/testify/suite" + + ibcclient "github.com/cosmos/ibc-go/v6/modules/core/02-client" + clientv7 "github.com/cosmos/ibc-go/v6/modules/core/02-client/migrations/v7" + clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v6/modules/core/24-host" + v7 "github.com/cosmos/ibc-go/v6/modules/core/migrations/v7" + "github.com/cosmos/ibc-go/v6/modules/core/types" + ibctesting "github.com/cosmos/ibc-go/v6/testing" +) + +type MigrationsV7TestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + // testing chains used for convenience and readability + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain +} + +// TestMigrationsV7TestSuite runs all the tests within this package. +func TestMigrationsV7TestSuite(t *testing.T) { + suite.Run(t, new(MigrationsV7TestSuite)) +} + +// SetupTest creates a coordinator with 2 test chains. +func (suite *MigrationsV7TestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) +} + +// NOTE: this test is mainly copied from 02-client/migrations/v7/genesis_test.go +func (suite *MigrationsV7TestSuite) TestMigrateGenesisSolomachine() { + // create tendermint clients + for i := 0; i < 3; i++ { + path := ibctesting.NewPath(suite.chainA, suite.chainB) + + suite.coordinator.SetupClients(path) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + // update a second time to add more state + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + } + + // create multiple legacy solo machine clients + solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1) + solomachineMulti := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4) + + clientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) + + // manually generate old proto buf definitions and set in genesis + // NOTE: we cannot use 'ExportGenesis' for the solo machines since we are + // using client states and consensus states which do not implement the exported.ClientState + // and exported.ConsensusState interface + var clients []clienttypes.IdentifiedClientState + for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} { + clientState := sm.ClientState() + + // generate old client state proto definition + legacyClientState := &clientv7.ClientState{ + Sequence: clientState.Sequence, + ConsensusState: &clientv7.ConsensusState{ + PublicKey: clientState.ConsensusState.PublicKey, + Diversifier: clientState.ConsensusState.Diversifier, + Timestamp: clientState.ConsensusState.Timestamp, + }, + AllowUpdateAfterProposal: true, + } + + // set client state + protoAny, err := codectypes.NewAnyWithValue(legacyClientState) + suite.Require().NoError(err) + suite.Require().NotNil(protoAny) + + clients = append(clients, clienttypes.IdentifiedClientState{ + ClientId: sm.ClientID, + ClientState: protoAny, + }) + + // set in store for ease of determining expected genesis + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), sm.ClientID) + bz, err := suite.chainA.App.AppCodec().MarshalInterface(legacyClientState) + suite.Require().NoError(err) + clientStore.Set(host.ClientStateKey(), bz) + + protoAny, err = codectypes.NewAnyWithValue(legacyClientState.ConsensusState) + suite.Require().NoError(err) + suite.Require().NotNil(protoAny) + + // obtain marshalled bytes to set in client store + bz, err = suite.chainA.App.AppCodec().MarshalInterface(legacyClientState.ConsensusState) + suite.Require().NoError(err) + + var consensusStates []clienttypes.ConsensusStateWithHeight + + // set consensus states in store and genesis + for i := uint64(0); i < 10; i++ { + height := clienttypes.NewHeight(1, i) + clientStore.Set(host.ConsensusStateKey(height), bz) + consensusStates = append(consensusStates, clienttypes.ConsensusStateWithHeight{ + Height: height, + ConsensusState: protoAny, + }) + } + + clientGenState.ClientsConsensus = append(clientGenState.ClientsConsensus, clienttypes.ClientConsensusStates{ + ClientId: sm.ClientID, + ConsensusStates: consensusStates, + }) + } + + // solo machine clients must come before tendermint in expected + clientGenState.Clients = append(clients, clientGenState.Clients...) + + // migrate store get expected genesis + // store migration and genesis migration should produce identical results + // NOTE: tendermint clients are not pruned in genesis so the test should not have expired tendermint clients + err := clientv7.MigrateStore(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetKey(host.StoreKey), suite.chainA.App.AppCodec()) + suite.Require().NoError(err) + expectedClientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) + + cdc := suite.chainA.App.AppCodec().(*codec.ProtoCodec) + + // NOTE: these lines are added in comparison to 02-client/migrations/v7/genesis_test.go + // generate appState with old ibc genesis state + appState := genutiltypes.AppMap{} + ibcGenState := types.DefaultGenesisState() + ibcGenState.ClientGenesis = clientGenState + + // ensure tests pass even if the legacy solo machine is already registered + clientv7.RegisterInterfaces(cdc.InterfaceRegistry()) + appState[host.ModuleName] = cdc.MustMarshalJSON(ibcGenState) + + // NOTE: genesis time isn't updated since we aren't testing for tendermint consensus state pruning + migrated, err := v7.MigrateGenesis(appState, cdc) + suite.Require().NoError(err) + + expectedAppState := genutiltypes.AppMap{} + expectedIBCGenState := types.DefaultGenesisState() + expectedIBCGenState.ClientGenesis = expectedClientGenState + + bz, err := cdc.MarshalJSON(expectedIBCGenState) + suite.Require().NoError(err) + expectedAppState[host.ModuleName] = bz + + suite.Require().Equal(expectedAppState, migrated) +} diff --git a/modules/core/migrations/v7/migrations.go b/modules/core/migrations/v7/migrations.go deleted file mode 100644 index 8eeda416eb4..00000000000 --- a/modules/core/migrations/v7/migrations.go +++ /dev/null @@ -1,44 +0,0 @@ -package v7 - -import ( - "strings" - - sdk "github.com/cosmos/cosmos-sdk/types" - - clientkeeper "github.com/cosmos/ibc-go/v6/modules/core/02-client/keeper" - clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v6/modules/core/24-host" - "github.com/cosmos/ibc-go/v6/modules/core/exported" -) - -// Localhost is the client type for a localhost client. It is also used as the clientID -// for the localhost client. -const Localhost string = "09-localhost" - -// MigrateToV7 prunes the 09-Localhost client and associated consensus states from the ibc store -func MigrateToV7(ctx sdk.Context, clientKeeper clientkeeper.Keeper) { - clientStore := clientKeeper.ClientStore(ctx, Localhost) - - iterator := sdk.KVStorePrefixIterator(clientStore, []byte(host.KeyConsensusStatePrefix)) - var heights []exported.Height - - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - keySplit := strings.Split(string(iterator.Key()), "/") - // key is in the format "consensusStates/" - if len(keySplit) != 2 || keySplit[0] != string(host.KeyConsensusStatePrefix) { - continue - } - - // collect consensus states to be pruned - heights = append(heights, clienttypes.MustParseHeight(keySplit[1])) - } - - // delete all consensus states - for _, height := range heights { - clientStore.Delete(host.ConsensusStateKey(height)) - } - - // delete the client state - clientStore.Delete(host.ClientStateKey()) -} diff --git a/modules/core/migrations/v7/migrations_test.go b/modules/core/migrations/v7/migrations_test.go deleted file mode 100644 index 53c18ae20d8..00000000000 --- a/modules/core/migrations/v7/migrations_test.go +++ /dev/null @@ -1,116 +0,0 @@ -package v7_test - -import ( - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/suite" - - clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v6/modules/core/24-host" - "github.com/cosmos/ibc-go/v6/modules/core/exported" - v7 "github.com/cosmos/ibc-go/v6/modules/core/migrations/v7" - ibctesting "github.com/cosmos/ibc-go/v6/testing" -) - -type MigrationsV7TestSuite struct { - suite.Suite - - coordinator *ibctesting.Coordinator - - chainA *ibctesting.TestChain - chainB *ibctesting.TestChain -} - -func (suite *MigrationsV7TestSuite) SetupTest() { - suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) - - suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) - suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) -} - -func TestIBCTestSuite(t *testing.T) { - suite.Run(t, new(MigrationsV7TestSuite)) -} - -func (suite *MigrationsV7TestSuite) TestMigrateToV7() { - var clientStore sdk.KVStore - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "success: prune localhost client state", - func() { - clientStore.Set(host.ClientStateKey(), []byte("clientState")) - }, - true, - }, - { - "success: prune localhost client state and consensus states", - func() { - clientStore.Set(host.ClientStateKey(), []byte("clientState")) - - for i := 0; i < 10; i++ { - clientStore.Set(host.ConsensusStateKey(clienttypes.NewHeight(1, uint64(i))), []byte("consensusState")) - } - }, - true, - }, - { - "07-tendermint client state and consensus states remain in client store", - func() { - clientStore = suite.chainA.GetSimApp().IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clienttypes.FormatClientIdentifier(exported.Tendermint, 0)) - clientStore.Set(host.ClientStateKey(), []byte("clientState")) - - for i := 0; i < 10; i++ { - clientStore.Set(host.ConsensusStateKey(clienttypes.NewHeight(1, uint64(i))), []byte("consensusState")) - } - }, - false, - }, - { - "06-solomachine client state and consensus states remain in client store", - func() { - clientStore = suite.chainA.GetSimApp().IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clienttypes.FormatClientIdentifier(exported.Solomachine, 0)) - clientStore.Set(host.ClientStateKey(), []byte("clientState")) - - for i := 0; i < 10; i++ { - clientStore.Set(host.ConsensusStateKey(clienttypes.NewHeight(1, uint64(i))), []byte("consensusState")) - } - }, - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - ctx := suite.chainA.GetContext() - clientStore = suite.chainA.GetSimApp().IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), v7.Localhost) - - tc.malleate() - - v7.MigrateToV7(ctx, suite.chainA.GetSimApp().IBCKeeper.ClientKeeper) - - if tc.expPass { - suite.Require().False(clientStore.Has(host.ClientStateKey())) - - for i := 0; i < 10; i++ { - suite.Require().False(clientStore.Has(host.ConsensusStateKey(clienttypes.NewHeight(1, uint64(i))))) - } - } else { - suite.Require().True(clientStore.Has(host.ClientStateKey())) - - for i := 0; i < 10; i++ { - suite.Require().True(clientStore.Has(host.ConsensusStateKey(clienttypes.NewHeight(1, uint64(i))))) - } - } - }) - } -} From 19c79ae32b98e3c2c66a7ebf47cd2efcf69b70b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Wed, 7 Dec 2022 12:57:09 +0100 Subject: [PATCH 12/44] refactor: simplify automatic migration code by using client keeper functions (#2864) --- modules/core/02-client/keeper/migrations.go | 2 +- .../migrations/v7/expected_keepers.go | 14 ++++++ .../02-client/migrations/v7/genesis_test.go | 2 +- modules/core/02-client/migrations/v7/store.go | 46 +++++++------------ .../02-client/migrations/v7/store_test.go | 23 +--------- modules/core/migrations/v7/genesis_test.go | 2 +- 6 files changed, 34 insertions(+), 55 deletions(-) create mode 100644 modules/core/02-client/migrations/v7/expected_keepers.go diff --git a/modules/core/02-client/keeper/migrations.go b/modules/core/02-client/keeper/migrations.go index c63a6762f5b..04b0b80cc8b 100644 --- a/modules/core/02-client/keeper/migrations.go +++ b/modules/core/02-client/keeper/migrations.go @@ -34,5 +34,5 @@ func (m Migrator) Migrate1to2(ctx sdk.Context) error { // - removes the localhost client // - asserts that existing tendermint clients are properly registered on the chain codec func (m Migrator) Migrate2to3(ctx sdk.Context) error { - return v7.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc) + return v7.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc, m.keeper) } diff --git a/modules/core/02-client/migrations/v7/expected_keepers.go b/modules/core/02-client/migrations/v7/expected_keepers.go new file mode 100644 index 00000000000..90f034f52c3 --- /dev/null +++ b/modules/core/02-client/migrations/v7/expected_keepers.go @@ -0,0 +1,14 @@ +package v7 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/v6/modules/core/exported" +) + +// ClientKeeper expected IBC client keeper +type ClientKeeper interface { + GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) + SetClientState(ctx sdk.Context, clientID string, clientState exported.ClientState) + ClientStore(ctx sdk.Context, clientID string) sdk.KVStore +} diff --git a/modules/core/02-client/migrations/v7/genesis_test.go b/modules/core/02-client/migrations/v7/genesis_test.go index 990fc7d367e..f758ed94aa9 100644 --- a/modules/core/02-client/migrations/v7/genesis_test.go +++ b/modules/core/02-client/migrations/v7/genesis_test.go @@ -101,7 +101,7 @@ func (suite *MigrationsV7TestSuite) TestMigrateGenesisSolomachine() { // migrate store get expected genesis // store migration and genesis migration should produce identical results // NOTE: tendermint clients are not pruned in genesis so the test should not have expired tendermint clients - err := v7.MigrateStore(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetKey(host.StoreKey), suite.chainA.App.AppCodec()) + err := v7.MigrateStore(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetKey(host.StoreKey), suite.chainA.App.AppCodec(), suite.chainA.GetSimApp().IBCKeeper.ClientKeeper) suite.Require().NoError(err) expectedClientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) diff --git a/modules/core/02-client/migrations/v7/store.go b/modules/core/02-client/migrations/v7/store.go index 2be30467000..20cb2ee2fd0 100644 --- a/modules/core/02-client/migrations/v7/store.go +++ b/modules/core/02-client/migrations/v7/store.go @@ -6,7 +6,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/store/prefix" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -29,18 +28,18 @@ const Localhost string = "09-localhost" // - Pruning all solo machine consensus states // - Removing the localhost client // - Asserting existing tendermint clients are properly registered on the chain codec -func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.BinaryCodec) error { +func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.BinaryCodec, clientKeeper ClientKeeper) error { store := ctx.KVStore(storeKey) - if err := handleSolomachineMigration(ctx, store, cdc); err != nil { + if err := handleSolomachineMigration(ctx, store, cdc, clientKeeper); err != nil { return err } - if err := handleTendermintMigration(ctx, store, cdc); err != nil { + if err := handleTendermintMigration(ctx, store, cdc, clientKeeper); err != nil { return err } - if err := handleLocalhostMigration(ctx, store, cdc); err != nil { + if err := handleLocalhostMigration(ctx, store, cdc, clientKeeper); err != nil { return err } @@ -49,15 +48,14 @@ func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.Binar // handleSolomachineMigration iterates over the solo machine clients and migrates client state from // protobuf definition v2 to v3. All consensus states stored outside of the client state are pruned. -func handleSolomachineMigration(ctx sdk.Context, store sdk.KVStore, cdc codec.BinaryCodec) error { +func handleSolomachineMigration(ctx sdk.Context, store sdk.KVStore, cdc codec.BinaryCodec, clientKeeper ClientKeeper) error { clients, err := collectClients(ctx, store, exported.Solomachine) if err != nil { return err } for _, clientID := range clients { - clientPrefix := []byte(fmt.Sprintf("%s/%s/", host.KeyClientStorePrefix, clientID)) - clientStore := prefix.NewStore(store, clientPrefix) + clientStore := clientKeeper.ClientStore(ctx, clientID) bz := clientStore.Get(host.ClientStateKey()) if bz == nil { @@ -76,13 +74,8 @@ func handleSolomachineMigration(ctx sdk.Context, store sdk.KVStore, cdc codec.Bi updatedClientState := migrateSolomachine(clientState) - bz, err := clienttypes.MarshalClientState(cdc, &updatedClientState) - if err != nil { - return sdkerrors.Wrap(err, "failed to unmarshal client state bytes into solo machine client state") - } - // update solomachine in store - clientStore.Set(host.ClientStateKey(), bz) + clientKeeper.SetClientState(ctx, clientID, &updatedClientState) removeAllClientConsensusStates(clientStore) } @@ -92,7 +85,7 @@ func handleSolomachineMigration(ctx sdk.Context, store sdk.KVStore, cdc codec.Bi // handlerTendermintMigration asserts that the tendermint client in state can be decoded properly. // This ensures the upgrading chain properly registered the tendermint client types on the chain codec. -func handleTendermintMigration(ctx sdk.Context, store sdk.KVStore, cdc codec.BinaryCodec) error { +func handleTendermintMigration(ctx sdk.Context, store sdk.KVStore, cdc codec.BinaryCodec, clientKeeper ClientKeeper) error { clients, err := collectClients(ctx, store, exported.Tendermint) if err != nil { return err @@ -104,20 +97,14 @@ func handleTendermintMigration(ctx sdk.Context, store sdk.KVStore, cdc codec.Bin clientID := clients[0] - clientPrefix := []byte(fmt.Sprintf("%s/%s/", host.KeyClientStorePrefix, clientID)) - clientStore := prefix.NewStore(store, clientPrefix) - - bz := clientStore.Get(host.ClientStateKey()) - if bz == nil { - return clienttypes.ErrClientNotFound - } - - var clientState exported.ClientState - if err := cdc.UnmarshalInterface(bz, &clientState); err != nil { - return sdkerrors.Wrap(err, "failed to unmarshal client state bytes into tendermint client state") + // unregistered tendermint client types will panic when unmarshaling the client state + // in GetClientState + clientState, ok := clientKeeper.GetClientState(ctx, clientID) + if !ok { + return sdkerrors.Wrapf(clienttypes.ErrClientNotFound, "clientID %s", clientID) } - _, ok := clientState.(*ibctm.ClientState) + _, ok = clientState.(*ibctm.ClientState) if !ok { return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "client state is not tendermint even though client id contains 07-tendermint") } @@ -126,15 +113,14 @@ func handleTendermintMigration(ctx sdk.Context, store sdk.KVStore, cdc codec.Bin } // handleLocalhostMigration removes all client and consensus states associated with the localhost client type. -func handleLocalhostMigration(ctx sdk.Context, store sdk.KVStore, cdc codec.BinaryCodec) error { +func handleLocalhostMigration(ctx sdk.Context, store sdk.KVStore, cdc codec.BinaryCodec, clientKeeper ClientKeeper) error { clients, err := collectClients(ctx, store, Localhost) if err != nil { return err } for _, clientID := range clients { - clientPrefix := []byte(fmt.Sprintf("%s/%s/", host.KeyClientStorePrefix, clientID)) - clientStore := prefix.NewStore(store, clientPrefix) + clientStore := clientKeeper.ClientStore(ctx, clientID) // delete the client state clientStore.Delete(host.ClientStateKey()) diff --git a/modules/core/02-client/migrations/v7/store_test.go b/modules/core/02-client/migrations/v7/store_test.go index c67425ea8bd..006ab637531 100644 --- a/modules/core/02-client/migrations/v7/store_test.go +++ b/modules/core/02-client/migrations/v7/store_test.go @@ -4,15 +4,11 @@ import ( "strconv" "testing" - "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/stretchr/testify/suite" "github.com/cosmos/ibc-go/v6/modules/core/02-client/migrations/v7" "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v6/modules/core/24-host" - solomachine "github.com/cosmos/ibc-go/v6/modules/light-clients/06-solomachine" - ibctm "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v6/testing" ) @@ -40,23 +36,6 @@ func TestIBCTestSuite(t *testing.T) { suite.Run(t, new(MigrationsV7TestSuite)) } -// test that MigrateStore returns an error if the codec used doesn't register tendermint types -func (suite *MigrationsV7TestSuite) TestMigrateStoreTendermint() { - path := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.SetupClients(path) - - registry := codectypes.NewInterfaceRegistry() - cdc := codec.NewProtoCodec(registry) - - solomachine.RegisterInterfaces(registry) - err := v7.MigrateStore(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetKey(host.StoreKey), cdc) - suite.Require().Error(err) - - ibctm.RegisterInterfaces(registry) - err = v7.MigrateStore(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetKey(host.StoreKey), cdc) - suite.Require().NoError(err) -} - // create multiple solo machine clients, tendermint and localhost clients // ensure that solo machine clients are migrated and their consensus states are removed // ensure the localhost is deleted entirely. @@ -79,7 +58,7 @@ func (suite *MigrationsV7TestSuite) TestMigrateStore() { suite.createSolomachineClients(solomachines) suite.createLocalhostClients() - err := v7.MigrateStore(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetKey(host.StoreKey), suite.chainA.App.AppCodec()) + err := v7.MigrateStore(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetKey(host.StoreKey), suite.chainA.App.AppCodec(), suite.chainA.GetSimApp().IBCKeeper.ClientKeeper) suite.Require().NoError(err) suite.assertSolomachineClients(solomachines) diff --git a/modules/core/migrations/v7/genesis_test.go b/modules/core/migrations/v7/genesis_test.go index 1ba9fcdf48d..82297d53cdf 100644 --- a/modules/core/migrations/v7/genesis_test.go +++ b/modules/core/migrations/v7/genesis_test.go @@ -128,7 +128,7 @@ func (suite *MigrationsV7TestSuite) TestMigrateGenesisSolomachine() { // migrate store get expected genesis // store migration and genesis migration should produce identical results // NOTE: tendermint clients are not pruned in genesis so the test should not have expired tendermint clients - err := clientv7.MigrateStore(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetKey(host.StoreKey), suite.chainA.App.AppCodec()) + err := clientv7.MigrateStore(suite.chainA.GetContext(), suite.chainA.GetSimApp().GetKey(host.StoreKey), suite.chainA.App.AppCodec(), suite.chainA.GetSimApp().IBCKeeper.ClientKeeper) suite.Require().NoError(err) expectedClientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) From 83854f487bf41add4e92e017bb7eab79aa948758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Wed, 7 Dec 2022 13:40:40 +0100 Subject: [PATCH 13/44] imp(api)!: remove legacy migrations required for upgrading from Stargate release line to ibc-go >= v1.x.x. (#2897) --- CHANGELOG.md | 1 + modules/core/02-client/keeper/migrations.go | 11 - modules/core/02-client/legacy/v100/genesis.go | 151 - .../02-client/legacy/v100/genesis_test.go | 307 -- .../core/02-client/legacy/v100/solomachine.go | 263 -- .../02-client/legacy/v100/solomachine.pb.go | 4121 ----------------- modules/core/02-client/legacy/v100/store.go | 172 - .../core/02-client/legacy/v100/store_test.go | 230 - modules/core/keeper/migrations.go | 15 - modules/core/module.go | 6 +- .../solomachine/v1/solomachine.proto | 189 - 11 files changed, 2 insertions(+), 5464 deletions(-) delete mode 100644 modules/core/02-client/legacy/v100/genesis.go delete mode 100644 modules/core/02-client/legacy/v100/genesis_test.go delete mode 100644 modules/core/02-client/legacy/v100/solomachine.go delete mode 100644 modules/core/02-client/legacy/v100/solomachine.pb.go delete mode 100644 modules/core/02-client/legacy/v100/store.go delete mode 100644 modules/core/02-client/legacy/v100/store_test.go delete mode 100644 proto/ibc/lightclients/solomachine/v1/solomachine.proto diff --git a/CHANGELOG.md b/CHANGELOG.md index a502dd7f70f..b85f401700f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### API Breaking +* (core) [\#2897](https://github.com/cosmos/ibc-go/pull/2897) Remove legacy migrations required for upgrading from Stargate release line to ibc-go >= v1.x.x. * (core/02-client) [\#2856](https://github.com/cosmos/ibc-go/pull/2856) Rename `IterateClients` to `IterateClientStates`. The function now takes a prefix argument which may be used for prefix iteration over the client store. * (apps/27-interchain-accounts) [\#2607](https://github.com/cosmos/ibc-go/pull/2607) `SerializeCosmosTx` now takes in a `[]proto.Message` instead of `[]sdk.Msg`. * (apps/transfer) [\#2446](https://github.com/cosmos/ibc-go/pull/2446) Remove `SendTransfer` function in favor of a private `sendTransfer` function. All IBC transfers must be initiated with `MsgTransfer`. diff --git a/modules/core/02-client/keeper/migrations.go b/modules/core/02-client/keeper/migrations.go index 04b0b80cc8b..a0ebb846ff7 100644 --- a/modules/core/02-client/keeper/migrations.go +++ b/modules/core/02-client/keeper/migrations.go @@ -3,7 +3,6 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - v100 "github.com/cosmos/ibc-go/v6/modules/core/02-client/legacy/v100" v7 "github.com/cosmos/ibc-go/v6/modules/core/02-client/migrations/v7" ) @@ -17,16 +16,6 @@ func NewMigrator(keeper Keeper) Migrator { return Migrator{keeper: keeper} } -// Migrate1to2 migrates from version 1 to 2. -// This migration -// - migrates solo machine client states from v1 to v2 protobuf definition -// - prunes solo machine consensus states -// - prunes expired tendermint consensus states -// - adds iteration and processed height keys for unexpired tendermint consensus states -func (m Migrator) Migrate1to2(ctx sdk.Context) error { - return v100.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc) -} - // Migrate2to3 migrates from version 2 to 3. // This migration // - migrates solo machine client states from v2 to v3 protobuf definition diff --git a/modules/core/02-client/legacy/v100/genesis.go b/modules/core/02-client/legacy/v100/genesis.go deleted file mode 100644 index 958a5edfa87..00000000000 --- a/modules/core/02-client/legacy/v100/genesis.go +++ /dev/null @@ -1,151 +0,0 @@ -package v100 - -import ( - "bytes" - "time" - - "github.com/cosmos/cosmos-sdk/codec" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v6/modules/core/24-host" - "github.com/cosmos/ibc-go/v6/modules/core/exported" - ibctm "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint" -) - -// MigrateGenesis accepts exported v1.0.0 IBC client genesis file and migrates it to: -// -// - Update solo machine client state protobuf definition (v1 to v2) -// - Remove all solo machine consensus states -// - Remove all expired tendermint consensus states -// - Adds ProcessedHeight and Iteration keys for unexpired tendermint consensus states -func MigrateGenesis(cdc codec.BinaryCodec, clientGenState *types.GenesisState, genesisBlockTime time.Time, selfHeight exported.Height) (*types.GenesisState, error) { - // To prune the consensus states, we will create new clientsConsensus - // and clientsMetadata. These slices will be filled up with consensus states - // which should not be pruned. No solo machine consensus states should be added - // and only unexpired consensus states for tendermint clients will be added. - // The metadata keys for unexpired consensus states will be added to clientsMetadata - var ( - clientsConsensus []types.ClientConsensusStates - clientsMetadata []types.IdentifiedGenesisMetadata - ) - - for i, client := range clientGenState.Clients { - clientType, _, err := types.ParseClientIdentifier(client.ClientId) - if err != nil { - return nil, err - } - - // update solo machine client state defintions - if clientType == exported.Solomachine { - clientState := &ClientState{} - if err := cdc.Unmarshal(client.ClientState.Value, clientState); err != nil { - return nil, sdkerrors.Wrap(err, "failed to unmarshal client state bytes into solo machine client state") - } - - updatedClientState := migrateSolomachine(clientState) - - any, err := types.PackClientState(updatedClientState) - if err != nil { - return nil, err - } - - clientGenState.Clients[i] = types.IdentifiedClientState{ - ClientId: client.ClientId, - ClientState: any, - } - } - - // iterate consensus states by client - for _, clientConsensusStates := range clientGenState.ClientsConsensus { - // look for consensus states for the current client - if clientConsensusStates.ClientId == client.ClientId { - switch clientType { - case exported.Solomachine: - // remove all consensus states for the solo machine - // do not add to new clientsConsensus - - case exported.Tendermint: - // only add non expired consensus states to new clientsConsensus - tmClientState, ok := client.ClientState.GetCachedValue().(*ibctm.ClientState) - if !ok { - return nil, types.ErrInvalidClient - } - - // collect unexpired consensus states - var unexpiredConsensusStates []types.ConsensusStateWithHeight - for _, consState := range clientConsensusStates.ConsensusStates { - tmConsState := consState.ConsensusState.GetCachedValue().(*ibctm.ConsensusState) - if !tmClientState.IsExpired(tmConsState.Timestamp, genesisBlockTime) { - unexpiredConsensusStates = append(unexpiredConsensusStates, consState) - } - } - - // if we found at least one unexpired consensus state, create a clientConsensusState - // and add it to clientsConsensus - if len(unexpiredConsensusStates) != 0 { - clientsConsensus = append(clientsConsensus, types.ClientConsensusStates{ - ClientId: client.ClientId, - ConsensusStates: unexpiredConsensusStates, - }) - } - - // collect metadata for unexpired consensus states - var clientMetadata []types.GenesisMetadata - - // remove all expired tendermint consensus state metadata by adding only - // unexpired consensus state metadata - for _, consState := range unexpiredConsensusStates { - for _, identifiedGenMetadata := range clientGenState.ClientsMetadata { - // look for metadata for current client - if identifiedGenMetadata.ClientId == client.ClientId { - - // obtain height for consensus state being pruned - height := consState.Height - - // iterate through metadata and find metadata for current unexpired height - // only unexpired consensus state metadata should be added - for _, metadata := range identifiedGenMetadata.ClientMetadata { - // the previous version of IBC only contained the processed time metadata - // if we find the processed time metadata for an unexpired height, add the - // iteration key and processed height keys. - if bytes.Equal(metadata.Key, ibctm.ProcessedTimeKey(height)) { - clientMetadata = append(clientMetadata, - // set the processed height using the current self height - // this is safe, it may cause delays in packet processing if there - // is a non zero connection delay time - types.GenesisMetadata{ - Key: ibctm.ProcessedHeightKey(height), - Value: []byte(selfHeight.String()), - }, - metadata, // processed time - types.GenesisMetadata{ - Key: ibctm.IterationKey(height), - Value: host.ConsensusStateKey(height), - }) - } - } - - } - } - } - - // if we have metadata for unexipred consensus states, add it to consensusMetadata - if len(clientMetadata) != 0 { - clientsMetadata = append(clientsMetadata, types.IdentifiedGenesisMetadata{ - ClientId: client.ClientId, - ClientMetadata: clientMetadata, - }) - } - - default: - break - } - } - } - } - - clientGenState.ClientsConsensus = clientsConsensus - clientGenState.ClientsMetadata = clientsMetadata - return clientGenState, nil -} diff --git a/modules/core/02-client/legacy/v100/genesis_test.go b/modules/core/02-client/legacy/v100/genesis_test.go deleted file mode 100644 index 42a887bd723..00000000000 --- a/modules/core/02-client/legacy/v100/genesis_test.go +++ /dev/null @@ -1,307 +0,0 @@ -package v100_test - -import ( - "bytes" - "encoding/json" - "time" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - - ibcclient "github.com/cosmos/ibc-go/v6/modules/core/02-client" - v100 "github.com/cosmos/ibc-go/v6/modules/core/02-client/legacy/v100" - "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v6/modules/core/24-host" - "github.com/cosmos/ibc-go/v6/modules/core/exported" - ibctm "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v6/testing" - "github.com/cosmos/ibc-go/v6/testing/simapp" -) - -func (suite *LegacyTestSuite) TestMigrateGenesisSolomachine() { - path := ibctesting.NewPath(suite.chainA, suite.chainB) - encodingConfig := simapp.MakeTestEncodingConfig() - clientCtx := client.Context{}. - WithInterfaceRegistry(encodingConfig.InterfaceRegistry). - WithTxConfig(encodingConfig.TxConfig). - WithCodec(encodingConfig.Marshaler) - - // create multiple legacy solo machine clients - solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1) - solomachineMulti := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4) - - // create tendermint clients - suite.coordinator.SetupClients(path) - err := path.EndpointA.UpdateClient() - suite.Require().NoError(err) - clientGenState := ibcclient.ExportGenesis(path.EndpointA.Chain.GetContext(), path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper) - - // manually generate old proto buf definitions and set in genesis - // NOTE: we cannot use 'ExportGenesis' for the solo machines since we are - // using client states and consensus states which do not implement the exported.ClientState - // and exported.ConsensusState interface - var clients []types.IdentifiedClientState - for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} { - clientState := sm.ClientState() - - var seq uint64 - if clientState.IsFrozen { - seq = 1 - } - - // generate old client state proto definition - legacyClientState := &v100.ClientState{ - Sequence: clientState.Sequence, - FrozenSequence: seq, - ConsensusState: &v100.ConsensusState{ - PublicKey: clientState.ConsensusState.PublicKey, - Diversifier: clientState.ConsensusState.Diversifier, - Timestamp: clientState.ConsensusState.Timestamp, - }, - } - - // set client state - any, err := codectypes.NewAnyWithValue(legacyClientState) - suite.Require().NoError(err) - suite.Require().NotNil(any) - client := types.IdentifiedClientState{ - ClientId: sm.ClientID, - ClientState: any, - } - clients = append(clients, client) - - // set in store for ease of determining expected genesis - clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(path.EndpointA.Chain.GetContext(), sm.ClientID) - bz, err := path.EndpointA.Chain.App.AppCodec().MarshalInterface(legacyClientState) - suite.Require().NoError(err) - clientStore.Set(host.ClientStateKey(), bz) - - // set some consensus states - height1 := types.NewHeight(0, 1) - height2 := types.NewHeight(1, 2) - height3 := types.NewHeight(0, 123) - - any, err = codectypes.NewAnyWithValue(legacyClientState.ConsensusState) - suite.Require().NoError(err) - suite.Require().NotNil(any) - consensusState1 := types.ConsensusStateWithHeight{ - Height: height1, - ConsensusState: any, - } - consensusState2 := types.ConsensusStateWithHeight{ - Height: height2, - ConsensusState: any, - } - consensusState3 := types.ConsensusStateWithHeight{ - Height: height3, - ConsensusState: any, - } - - clientConsensusState := types.ClientConsensusStates{ - ClientId: sm.ClientID, - ConsensusStates: []types.ConsensusStateWithHeight{consensusState1, consensusState2, consensusState3}, - } - - clientGenState.ClientsConsensus = append(clientGenState.ClientsConsensus, clientConsensusState) - - // set in store for ease of determining expected genesis - bz, err = path.EndpointA.Chain.App.AppCodec().MarshalInterface(legacyClientState.ConsensusState) - suite.Require().NoError(err) - clientStore.Set(host.ConsensusStateKey(height1), bz) - clientStore.Set(host.ConsensusStateKey(height2), bz) - clientStore.Set(host.ConsensusStateKey(height3), bz) - } - // solo machine clients must come before tendermint in expected - clientGenState.Clients = append(clients, clientGenState.Clients...) - - // migrate store get expected genesis - // store migration and genesis migration should produce identical results - err = v100.MigrateStore(path.EndpointA.Chain.GetContext(), path.EndpointA.Chain.GetSimApp().GetKey(host.StoreKey), path.EndpointA.Chain.App.AppCodec()) - suite.Require().NoError(err) - expectedClientGenState := ibcclient.ExportGenesis(path.EndpointA.Chain.GetContext(), path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper) - - // NOTE: genesis time isn't updated since we aren't testing for tendermint consensus state pruning - migrated, err := v100.MigrateGenesis(codec.NewProtoCodec(clientCtx.InterfaceRegistry), &clientGenState, suite.coordinator.CurrentTime, types.GetSelfHeight(suite.chainA.GetContext())) - suite.Require().NoError(err) - - // 'ExportGenesis' order metadata keys by processedheight, processedtime for all heights, then it appends all iteration keys - // In order to match the genesis migration with export genesis (from store migrations) we must reorder the iteration keys to be last - // This isn't ideal, but it is better than modifying the genesis migration from a previous version to match the export genesis of a new version - // which provides no benefit except nicer testing - for i, clientMetadata := range migrated.ClientsMetadata { - var updatedMetadata []types.GenesisMetadata - var iterationKeys []types.GenesisMetadata - for _, metadata := range clientMetadata.ClientMetadata { - if bytes.HasPrefix(metadata.Key, []byte(ibctm.KeyIterateConsensusStatePrefix)) { - iterationKeys = append(iterationKeys, metadata) - } else { - updatedMetadata = append(updatedMetadata, metadata) - } - } - updatedMetadata = append(updatedMetadata, iterationKeys...) - migrated.ClientsMetadata[i] = types.IdentifiedGenesisMetadata{ - ClientId: clientMetadata.ClientId, - ClientMetadata: updatedMetadata, - } - } - - bz, err := clientCtx.Codec.MarshalJSON(&expectedClientGenState) - suite.Require().NoError(err) - - // Indent the JSON bz correctly. - var jsonObj map[string]interface{} - err = json.Unmarshal(bz, &jsonObj) - suite.Require().NoError(err) - expectedIndentedBz, err := json.MarshalIndent(jsonObj, "", "\t") - suite.Require().NoError(err) - - bz, err = clientCtx.Codec.MarshalJSON(migrated) - suite.Require().NoError(err) - - // Indent the JSON bz correctly. - err = json.Unmarshal(bz, &jsonObj) - suite.Require().NoError(err) - indentedBz, err := json.MarshalIndent(jsonObj, "", "\t") - suite.Require().NoError(err) - - suite.Require().Equal(string(expectedIndentedBz), string(indentedBz)) -} - -func (suite *LegacyTestSuite) TestMigrateGenesisTendermint() { - // create two paths and setup clients - path1 := ibctesting.NewPath(suite.chainA, suite.chainB) - path2 := ibctesting.NewPath(suite.chainA, suite.chainB) - encodingConfig := simapp.MakeTestEncodingConfig() - clientCtx := client.Context{}. - WithInterfaceRegistry(encodingConfig.InterfaceRegistry). - WithTxConfig(encodingConfig.TxConfig). - WithCodec(encodingConfig.Marshaler) - - suite.coordinator.SetupClients(path1) - suite.coordinator.SetupClients(path2) - - // collect all heights expected to be pruned - var path1PruneHeights, path2PruneHeights []exported.Height - path1PruneHeights = append(path1PruneHeights, path1.EndpointA.GetClientState().GetLatestHeight()) - path2PruneHeights = append(path2PruneHeights, path2.EndpointA.GetClientState().GetLatestHeight()) - - // these heights will be expired and also pruned - for i := 0; i < 3; i++ { - path1.EndpointA.UpdateClient() - path1PruneHeights = append(path1PruneHeights, path1.EndpointA.GetClientState().GetLatestHeight()) - } - for i := 0; i < 3; i++ { - path2.EndpointA.UpdateClient() - path2PruneHeights = append(path2PruneHeights, path2.EndpointA.GetClientState().GetLatestHeight()) - } - - // Increment the time by a week - suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) - - // create the consensus state that can be used as trusted height for next update - path1.EndpointA.UpdateClient() - path1.EndpointA.UpdateClient() - path2.EndpointA.UpdateClient() - path2.EndpointA.UpdateClient() - - clientGenState := ibcclient.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) - suite.Require().NotNil(clientGenState.Clients) - suite.Require().NotNil(clientGenState.ClientsConsensus) - suite.Require().NotNil(clientGenState.ClientsMetadata) - - // Increment the time by another week, then update the client. - // This will cause the consensus states created before the first time increment - // to be expired - suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) - - // migrate store get expected genesis - // store migration and genesis migration should produce identical results - err := v100.MigrateStore(path1.EndpointA.Chain.GetContext(), path1.EndpointA.Chain.GetSimApp().GetKey(host.StoreKey), path1.EndpointA.Chain.App.AppCodec()) - suite.Require().NoError(err) - expectedClientGenState := ibcclient.ExportGenesis(path1.EndpointA.Chain.GetContext(), path1.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper) - - migrated, err := v100.MigrateGenesis(codec.NewProtoCodec(clientCtx.InterfaceRegistry), &clientGenState, suite.coordinator.CurrentTime, types.GetSelfHeight(suite.chainA.GetContext())) - suite.Require().NoError(err) - - // 'ExportGenesis' order metadata keys by processedheight, processedtime for all heights, then it appends all iteration keys - // In order to match the genesis migration with export genesis we must reorder the iteration keys to be last - // This isn't ideal, but it is better than modifying the genesis migration from a previous version to match the export genesis of a new version - // which provides no benefit except nicer testing - for i, clientMetadata := range migrated.ClientsMetadata { - var updatedMetadata []types.GenesisMetadata - var iterationKeys []types.GenesisMetadata - for _, metadata := range clientMetadata.ClientMetadata { - if bytes.HasPrefix(metadata.Key, []byte(ibctm.KeyIterateConsensusStatePrefix)) { - iterationKeys = append(iterationKeys, metadata) - } else { - updatedMetadata = append(updatedMetadata, metadata) - } - } - updatedMetadata = append(updatedMetadata, iterationKeys...) - migrated.ClientsMetadata[i] = types.IdentifiedGenesisMetadata{ - ClientId: clientMetadata.ClientId, - ClientMetadata: updatedMetadata, - } - } - - // check path 1 client pruning - for _, height := range path1PruneHeights { - for _, client := range migrated.ClientsConsensus { - if client.ClientId == path1.EndpointA.ClientID { - for _, consensusState := range client.ConsensusStates { - suite.Require().NotEqual(height, consensusState.Height) - } - } - } - for _, client := range migrated.ClientsMetadata { - if client.ClientId == path1.EndpointA.ClientID { - for _, metadata := range client.ClientMetadata { - suite.Require().NotEqual(ibctm.ProcessedTimeKey(height), metadata.Key) - suite.Require().NotEqual(ibctm.ProcessedHeightKey(height), metadata.Key) - suite.Require().NotEqual(ibctm.IterationKey(height), metadata.Key) - } - } - } - } - - // check path 2 client pruning - for _, height := range path2PruneHeights { - for _, client := range migrated.ClientsConsensus { - if client.ClientId == path2.EndpointA.ClientID { - for _, consensusState := range client.ConsensusStates { - suite.Require().NotEqual(height, consensusState.Height) - } - } - } - for _, client := range migrated.ClientsMetadata { - if client.ClientId == path2.EndpointA.ClientID { - for _, metadata := range client.ClientMetadata { - suite.Require().NotEqual(ibctm.ProcessedTimeKey(height), metadata.Key) - suite.Require().NotEqual(ibctm.ProcessedHeightKey(height), metadata.Key) - suite.Require().NotEqual(ibctm.IterationKey(height), metadata.Key) - } - } - } - } - bz, err := clientCtx.Codec.MarshalJSON(&expectedClientGenState) - suite.Require().NoError(err) - - // Indent the JSON bz correctly. - var jsonObj map[string]interface{} - err = json.Unmarshal(bz, &jsonObj) - suite.Require().NoError(err) - expectedIndentedBz, err := json.MarshalIndent(jsonObj, "", "\t") - suite.Require().NoError(err) - - bz, err = clientCtx.Codec.MarshalJSON(migrated) - suite.Require().NoError(err) - - // Indent the JSON bz correctly. - err = json.Unmarshal(bz, &jsonObj) - suite.Require().NoError(err) - indentedBz, err := json.MarshalIndent(jsonObj, "", "\t") - suite.Require().NoError(err) - - suite.Require().Equal(string(expectedIndentedBz), string(indentedBz)) -} diff --git a/modules/core/02-client/legacy/v100/solomachine.go b/modules/core/02-client/legacy/v100/solomachine.go deleted file mode 100644 index c953fc419b6..00000000000 --- a/modules/core/02-client/legacy/v100/solomachine.go +++ /dev/null @@ -1,263 +0,0 @@ -package v100 - -import ( - ics23 "github.com/confio/ics23/go" - "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/cosmos/ibc-go/v6/modules/core/exported" -) - -// NOTE: this is a mock implmentation for exported.ClientState. This implementation -// should only be registered on the InterfaceRegistry during cli command genesis migration. -// This implementation is only used to successfully unmarshal the previous solo machine -// client state and consensus state and migrate them to the new implementations. When the proto -// codec unmarshals, it calls UnpackInterfaces() to create a cached value of the any. The -// UnpackInterfaces function for IdenitifiedClientState will attempt to unpack the any to -// exported.ClientState. If the solomachine v1 type is not registered against the exported.ClientState -// the unmarshal will fail. This implementation will panic on every interface function. -// The same is done for the ConsensusState. - -// Interface implementation checks. -var ( - _, _ codectypes.UnpackInterfacesMessage = &ClientState{}, &ConsensusState{} - _ exported.ClientState = (*ClientState)(nil) - _ exported.ConsensusState = &ConsensusState{} -) - -func RegisterInterfaces(registry codectypes.InterfaceRegistry) { - registry.RegisterImplementations( - (*exported.ClientState)(nil), - &ClientState{}, - ) - registry.RegisterImplementations( - (*exported.ConsensusState)(nil), - &ConsensusState{}, - ) -} - -// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method -func (cs ClientState) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { - return cs.ConsensusState.UnpackInterfaces(unpacker) -} - -// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method -func (cs ConsensusState) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { - return unpacker.UnpackAny(cs.PublicKey, new(cryptotypes.PubKey)) -} - -// ClientType panics! -func (cs ClientState) ClientType() string { - panic("legacy solo machine is deprecated!") -} - -// GetLatestHeight panics! -func (cs ClientState) GetLatestHeight() exported.Height { - panic("legacy solo machine is deprecated!") -} - -// Status panics! -func (cs ClientState) Status(_ sdk.Context, _ sdk.KVStore, _ codec.BinaryCodec) exported.Status { - panic("legacy solo machine is deprecated!") -} - -// Validate panics! -func (cs ClientState) Validate() error { - panic("legacy solo machine is deprecated!") -} - -// GetProofSpecs panics! -func (cs ClientState) GetProofSpecs() []*ics23.ProofSpec { - panic("legacy solo machine is deprecated!") -} - -// ZeroCustomFields panics! -func (cs ClientState) ZeroCustomFields() exported.ClientState { - panic("legacy solo machine is deprecated!") -} - -// Initialize panics! -func (cs ClientState) Initialize(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, consState exported.ConsensusState) error { - panic("legacy solo machine is deprecated!") -} - -// ExportMetadata panics! -func (cs ClientState) ExportMetadata(_ sdk.KVStore) []exported.GenesisMetadata { - panic("legacy solo machine is deprecated!") -} - -// CheckForMisbehaviour panics! -func (cs ClientState) CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, msg exported.ClientMessage) bool { - panic("legacy solo machine is deprecated!") -} - -// UpdateStateOnMisbehaviour panics! -func (cs *ClientState) UpdateStateOnMisbehaviour( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, -) { - panic("legacy solo machine is deprecated!") -} - -// VerifyClientMessage panics! -func (cs *ClientState) VerifyClientMessage( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, -) error { - panic("legacy solo machine is deprecated!") -} - -// UpdateState panis! -func (cs *ClientState) UpdateState(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage) []exported.Height { - panic("legacy solo machine is deprecated!") -} - -// CheckHeaderAndUpdateState panics! -func (cs *ClientState) CheckHeaderAndUpdateState( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, -) (exported.ClientState, exported.ConsensusState, error) { - panic("legacy solo machine is deprecated!") -} - -// CheckMisbehaviourAndUpdateState panics! -func (cs ClientState) CheckMisbehaviourAndUpdateState( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, -) (exported.ClientState, error) { - panic("legacy solo machine is deprecated!") -} - -// CheckSubstituteAndUpdateState panics! -func (cs ClientState) CheckSubstituteAndUpdateState( - ctx sdk.Context, _ codec.BinaryCodec, _, _ sdk.KVStore, - _ exported.ClientState, -) error { - panic("legacy solo machine is deprecated!") -} - -// VerifyUpgradeAndUpdateState panics! -func (cs ClientState) VerifyUpgradeAndUpdateState( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, - _ exported.ClientState, _ exported.ConsensusState, _, _ []byte, -) error { - panic("legacy solo machine is deprecated!") -} - -// VerifyClientState panics! -func (cs ClientState) VerifyClientState( - store sdk.KVStore, cdc codec.BinaryCodec, - _ exported.Height, _ exported.Prefix, _ string, _ []byte, clientState exported.ClientState, -) error { - panic("legacy solo machine is deprecated!") -} - -// VerifyClientConsensusState panics! -func (cs ClientState) VerifyClientConsensusState( - sdk.KVStore, codec.BinaryCodec, - exported.Height, string, exported.Height, exported.Prefix, - []byte, exported.ConsensusState, -) error { - panic("legacy solo machine is deprecated!") -} - -// VerifyConnectionState panics! -func (cs ClientState) VerifyConnectionState( - sdk.KVStore, codec.BinaryCodec, exported.Height, - exported.Prefix, []byte, string, exported.ConnectionI, -) error { - panic("legacy solo machine is deprecated!") -} - -// VerifyChannelState panics! -func (cs ClientState) VerifyChannelState( - sdk.KVStore, codec.BinaryCodec, exported.Height, exported.Prefix, - []byte, string, string, exported.ChannelI, -) error { - panic("legacy solo machine is deprecated!") -} - -// VerifyPacketCommitment panics! -func (cs ClientState) VerifyPacketCommitment( - sdk.Context, sdk.KVStore, codec.BinaryCodec, exported.Height, - uint64, uint64, exported.Prefix, []byte, - string, string, uint64, []byte, -) error { - panic("legacy solo machine is deprecated!") -} - -// VerifyPacketAcknowledgement panics! -func (cs ClientState) VerifyPacketAcknowledgement( - sdk.Context, sdk.KVStore, codec.BinaryCodec, exported.Height, - uint64, uint64, exported.Prefix, []byte, - string, string, uint64, []byte, -) error { - panic("legacy solo machine is deprecated!") -} - -// VerifyPacketReceiptAbsence panics! -func (cs ClientState) VerifyPacketReceiptAbsence( - sdk.Context, sdk.KVStore, codec.BinaryCodec, exported.Height, - uint64, uint64, exported.Prefix, []byte, - string, string, uint64, -) error { - panic("legacy solo machine is deprecated!") -} - -// VerifyNextSequenceRecv panics! -func (cs ClientState) VerifyNextSequenceRecv( - sdk.Context, sdk.KVStore, codec.BinaryCodec, exported.Height, - uint64, uint64, exported.Prefix, []byte, - string, string, uint64, -) error { - panic("legacy solo machine is deprecated!") -} - -// GetTimestampAtHeight panics! -func (cs ClientState) GetTimestampAtHeight( - sdk.Context, sdk.KVStore, codec.BinaryCodec, exported.Height, -) (uint64, error) { - panic("legacy solo machine is deprecated!") -} - -// VerifyMembership panics! -func (cs *ClientState) VerifyMembership( - ctx sdk.Context, - clientStore sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - proof []byte, - path exported.Path, - value []byte, -) error { - panic("legacy solo machine is deprecated!") -} - -// VerifyNonMembership panics! -func (cs *ClientState) VerifyNonMembership( - ctx sdk.Context, - clientStore sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - proof []byte, - path exported.Path, -) error { - panic("legacy solo machine is deprecated") -} - -// ClientType panics! -func (ConsensusState) ClientType() string { - panic("legacy solo machine is deprecated!") -} - -// GetTimestamp panics! -func (cs ConsensusState) GetTimestamp() uint64 { - panic("legacy solo machine is deprecated!") -} - -// ValidateBasic panics! -func (cs ConsensusState) ValidateBasic() error { - panic("legacy solo machine is deprecated!") -} diff --git a/modules/core/02-client/legacy/v100/solomachine.pb.go b/modules/core/02-client/legacy/v100/solomachine.pb.go deleted file mode 100644 index 2c4715dc633..00000000000 --- a/modules/core/02-client/legacy/v100/solomachine.pb.go +++ /dev/null @@ -1,4121 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: ibc/lightclients/solomachine/v1/solomachine.proto - -package v100 - -import ( - fmt "fmt" - types "github.com/cosmos/cosmos-sdk/codec/types" - types1 "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types" - types2 "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" - io "io" - math "math" - math_bits "math/bits" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package - -// DataType defines the type of solo machine proof being created. This is done -// to preserve uniqueness of different data sign byte encodings. -type DataType int32 - -const ( - // Default State - UNSPECIFIED DataType = 0 - // Data type for client state verification - CLIENT DataType = 1 - // Data type for consensus state verification - CONSENSUS DataType = 2 - // Data type for connection state verification - CONNECTION DataType = 3 - // Data type for channel state verification - CHANNEL DataType = 4 - // Data type for packet commitment verification - PACKETCOMMITMENT DataType = 5 - // Data type for packet acknowledgement verification - PACKETACKNOWLEDGEMENT DataType = 6 - // Data type for packet receipt absence verification - PACKETRECEIPTABSENCE DataType = 7 - // Data type for next sequence recv verification - NEXTSEQUENCERECV DataType = 8 - // Data type for header verification - HEADER DataType = 9 -) - -var DataType_name = map[int32]string{ - 0: "DATA_TYPE_UNINITIALIZED_UNSPECIFIED", - 1: "DATA_TYPE_CLIENT_STATE", - 2: "DATA_TYPE_CONSENSUS_STATE", - 3: "DATA_TYPE_CONNECTION_STATE", - 4: "DATA_TYPE_CHANNEL_STATE", - 5: "DATA_TYPE_PACKET_COMMITMENT", - 6: "DATA_TYPE_PACKET_ACKNOWLEDGEMENT", - 7: "DATA_TYPE_PACKET_RECEIPT_ABSENCE", - 8: "DATA_TYPE_NEXT_SEQUENCE_RECV", - 9: "DATA_TYPE_HEADER", -} - -var DataType_value = map[string]int32{ - "DATA_TYPE_UNINITIALIZED_UNSPECIFIED": 0, - "DATA_TYPE_CLIENT_STATE": 1, - "DATA_TYPE_CONSENSUS_STATE": 2, - "DATA_TYPE_CONNECTION_STATE": 3, - "DATA_TYPE_CHANNEL_STATE": 4, - "DATA_TYPE_PACKET_COMMITMENT": 5, - "DATA_TYPE_PACKET_ACKNOWLEDGEMENT": 6, - "DATA_TYPE_PACKET_RECEIPT_ABSENCE": 7, - "DATA_TYPE_NEXT_SEQUENCE_RECV": 8, - "DATA_TYPE_HEADER": 9, -} - -func (x DataType) String() string { - return proto.EnumName(DataType_name, int32(x)) -} - -func (DataType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{0} -} - -// ClientState defines a solo machine client that tracks the current consensus -// state and if the client is frozen. -type ClientState struct { - // latest sequence of the client state - Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` - // frozen sequence of the solo machine - FrozenSequence uint64 `protobuf:"varint,2,opt,name=frozen_sequence,json=frozenSequence,proto3" json:"frozen_sequence,omitempty" yaml:"frozen_sequence"` - ConsensusState *ConsensusState `protobuf:"bytes,3,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty" yaml:"consensus_state"` - // when set to true, will allow governance to update a solo machine client. - // The client will be unfrozen if it is frozen. - AllowUpdateAfterProposal bool `protobuf:"varint,4,opt,name=allow_update_after_proposal,json=allowUpdateAfterProposal,proto3" json:"allow_update_after_proposal,omitempty" yaml:"allow_update_after_proposal"` -} - -func (m *ClientState) Reset() { *m = ClientState{} } -func (m *ClientState) String() string { return proto.CompactTextString(m) } -func (*ClientState) ProtoMessage() {} -func (*ClientState) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{0} -} -func (m *ClientState) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ClientState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ClientState.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 *ClientState) XXX_Merge(src proto.Message) { - xxx_messageInfo_ClientState.Merge(m, src) -} -func (m *ClientState) XXX_Size() int { - return m.Size() -} -func (m *ClientState) XXX_DiscardUnknown() { - xxx_messageInfo_ClientState.DiscardUnknown(m) -} - -var xxx_messageInfo_ClientState proto.InternalMessageInfo - -// ConsensusState defines a solo machine consensus state. The sequence of a -// consensus state is contained in the "height" key used in storing the -// consensus state. -type ConsensusState struct { - // public key of the solo machine - PublicKey *types.Any `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty" yaml:"public_key"` - // diversifier allows the same public key to be re-used across different solo - // machine clients (potentially on different chains) without being considered - // misbehaviour. - Diversifier string `protobuf:"bytes,2,opt,name=diversifier,proto3" json:"diversifier,omitempty"` - Timestamp uint64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` -} - -func (m *ConsensusState) Reset() { *m = ConsensusState{} } -func (m *ConsensusState) String() string { return proto.CompactTextString(m) } -func (*ConsensusState) ProtoMessage() {} -func (*ConsensusState) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{1} -} -func (m *ConsensusState) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ConsensusState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ConsensusState.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 *ConsensusState) XXX_Merge(src proto.Message) { - xxx_messageInfo_ConsensusState.Merge(m, src) -} -func (m *ConsensusState) XXX_Size() int { - return m.Size() -} -func (m *ConsensusState) XXX_DiscardUnknown() { - xxx_messageInfo_ConsensusState.DiscardUnknown(m) -} - -var xxx_messageInfo_ConsensusState proto.InternalMessageInfo - -// Header defines a solo machine consensus header -type Header struct { - // sequence to update solo machine public key at - Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` - Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` - NewPublicKey *types.Any `protobuf:"bytes,4,opt,name=new_public_key,json=newPublicKey,proto3" json:"new_public_key,omitempty" yaml:"new_public_key"` - NewDiversifier string `protobuf:"bytes,5,opt,name=new_diversifier,json=newDiversifier,proto3" json:"new_diversifier,omitempty" yaml:"new_diversifier"` -} - -func (m *Header) Reset() { *m = Header{} } -func (m *Header) String() string { return proto.CompactTextString(m) } -func (*Header) ProtoMessage() {} -func (*Header) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{2} -} -func (m *Header) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *Header) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_Header.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 *Header) XXX_Merge(src proto.Message) { - xxx_messageInfo_Header.Merge(m, src) -} -func (m *Header) XXX_Size() int { - return m.Size() -} -func (m *Header) XXX_DiscardUnknown() { - xxx_messageInfo_Header.DiscardUnknown(m) -} - -var xxx_messageInfo_Header proto.InternalMessageInfo - -// Misbehaviour defines misbehaviour for a solo machine which consists -// of a sequence and two signatures over different messages at that sequence. -type Misbehaviour struct { - ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` - Sequence uint64 `protobuf:"varint,2,opt,name=sequence,proto3" json:"sequence,omitempty"` - SignatureOne *SignatureAndData `protobuf:"bytes,3,opt,name=signature_one,json=signatureOne,proto3" json:"signature_one,omitempty" yaml:"signature_one"` - SignatureTwo *SignatureAndData `protobuf:"bytes,4,opt,name=signature_two,json=signatureTwo,proto3" json:"signature_two,omitempty" yaml:"signature_two"` -} - -func (m *Misbehaviour) Reset() { *m = Misbehaviour{} } -func (m *Misbehaviour) String() string { return proto.CompactTextString(m) } -func (*Misbehaviour) ProtoMessage() {} -func (*Misbehaviour) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{3} -} -func (m *Misbehaviour) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *Misbehaviour) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_Misbehaviour.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 *Misbehaviour) XXX_Merge(src proto.Message) { - xxx_messageInfo_Misbehaviour.Merge(m, src) -} -func (m *Misbehaviour) XXX_Size() int { - return m.Size() -} -func (m *Misbehaviour) XXX_DiscardUnknown() { - xxx_messageInfo_Misbehaviour.DiscardUnknown(m) -} - -var xxx_messageInfo_Misbehaviour proto.InternalMessageInfo - -// SignatureAndData contains a signature and the data signed over to create that -// signature. -type SignatureAndData struct { - Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` - DataType DataType `protobuf:"varint,2,opt,name=data_type,json=dataType,proto3,enum=ibc.lightclients.solomachine.v1.DataType" json:"data_type,omitempty" yaml:"data_type"` - Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` - Timestamp uint64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` -} - -func (m *SignatureAndData) Reset() { *m = SignatureAndData{} } -func (m *SignatureAndData) String() string { return proto.CompactTextString(m) } -func (*SignatureAndData) ProtoMessage() {} -func (*SignatureAndData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{4} -} -func (m *SignatureAndData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *SignatureAndData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_SignatureAndData.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 *SignatureAndData) XXX_Merge(src proto.Message) { - xxx_messageInfo_SignatureAndData.Merge(m, src) -} -func (m *SignatureAndData) XXX_Size() int { - return m.Size() -} -func (m *SignatureAndData) XXX_DiscardUnknown() { - xxx_messageInfo_SignatureAndData.DiscardUnknown(m) -} - -var xxx_messageInfo_SignatureAndData proto.InternalMessageInfo - -// TimestampedSignatureData contains the signature data and the timestamp of the -// signature. -type TimestampedSignatureData struct { - SignatureData []byte `protobuf:"bytes,1,opt,name=signature_data,json=signatureData,proto3" json:"signature_data,omitempty" yaml:"signature_data"` - Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` -} - -func (m *TimestampedSignatureData) Reset() { *m = TimestampedSignatureData{} } -func (m *TimestampedSignatureData) String() string { return proto.CompactTextString(m) } -func (*TimestampedSignatureData) ProtoMessage() {} -func (*TimestampedSignatureData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{5} -} -func (m *TimestampedSignatureData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *TimestampedSignatureData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_TimestampedSignatureData.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 *TimestampedSignatureData) XXX_Merge(src proto.Message) { - xxx_messageInfo_TimestampedSignatureData.Merge(m, src) -} -func (m *TimestampedSignatureData) XXX_Size() int { - return m.Size() -} -func (m *TimestampedSignatureData) XXX_DiscardUnknown() { - xxx_messageInfo_TimestampedSignatureData.DiscardUnknown(m) -} - -var xxx_messageInfo_TimestampedSignatureData proto.InternalMessageInfo - -// SignBytes defines the signed bytes used for signature verification. -type SignBytes struct { - Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` - Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - Diversifier string `protobuf:"bytes,3,opt,name=diversifier,proto3" json:"diversifier,omitempty"` - // type of the data used - DataType DataType `protobuf:"varint,4,opt,name=data_type,json=dataType,proto3,enum=ibc.lightclients.solomachine.v1.DataType" json:"data_type,omitempty" yaml:"data_type"` - // marshaled data - Data []byte `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` -} - -func (m *SignBytes) Reset() { *m = SignBytes{} } -func (m *SignBytes) String() string { return proto.CompactTextString(m) } -func (*SignBytes) ProtoMessage() {} -func (*SignBytes) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{6} -} -func (m *SignBytes) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *SignBytes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_SignBytes.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 *SignBytes) XXX_Merge(src proto.Message) { - xxx_messageInfo_SignBytes.Merge(m, src) -} -func (m *SignBytes) XXX_Size() int { - return m.Size() -} -func (m *SignBytes) XXX_DiscardUnknown() { - xxx_messageInfo_SignBytes.DiscardUnknown(m) -} - -var xxx_messageInfo_SignBytes proto.InternalMessageInfo - -// HeaderData returns the SignBytes data for update verification. -type HeaderData struct { - // header public key - NewPubKey *types.Any `protobuf:"bytes,1,opt,name=new_pub_key,json=newPubKey,proto3" json:"new_pub_key,omitempty" yaml:"new_pub_key"` - // header diversifier - NewDiversifier string `protobuf:"bytes,2,opt,name=new_diversifier,json=newDiversifier,proto3" json:"new_diversifier,omitempty" yaml:"new_diversifier"` -} - -func (m *HeaderData) Reset() { *m = HeaderData{} } -func (m *HeaderData) String() string { return proto.CompactTextString(m) } -func (*HeaderData) ProtoMessage() {} -func (*HeaderData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{7} -} -func (m *HeaderData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *HeaderData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_HeaderData.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 *HeaderData) XXX_Merge(src proto.Message) { - xxx_messageInfo_HeaderData.Merge(m, src) -} -func (m *HeaderData) XXX_Size() int { - return m.Size() -} -func (m *HeaderData) XXX_DiscardUnknown() { - xxx_messageInfo_HeaderData.DiscardUnknown(m) -} - -var xxx_messageInfo_HeaderData proto.InternalMessageInfo - -// ClientStateData returns the SignBytes data for client state verification. -type ClientStateData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - ClientState *types.Any `protobuf:"bytes,2,opt,name=client_state,json=clientState,proto3" json:"client_state,omitempty" yaml:"client_state"` -} - -func (m *ClientStateData) Reset() { *m = ClientStateData{} } -func (m *ClientStateData) String() string { return proto.CompactTextString(m) } -func (*ClientStateData) ProtoMessage() {} -func (*ClientStateData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{8} -} -func (m *ClientStateData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ClientStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ClientStateData.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 *ClientStateData) XXX_Merge(src proto.Message) { - xxx_messageInfo_ClientStateData.Merge(m, src) -} -func (m *ClientStateData) XXX_Size() int { - return m.Size() -} -func (m *ClientStateData) XXX_DiscardUnknown() { - xxx_messageInfo_ClientStateData.DiscardUnknown(m) -} - -var xxx_messageInfo_ClientStateData proto.InternalMessageInfo - -// ConsensusStateData returns the SignBytes data for consensus state -// verification. -type ConsensusStateData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - ConsensusState *types.Any `protobuf:"bytes,2,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty" yaml:"consensus_state"` -} - -func (m *ConsensusStateData) Reset() { *m = ConsensusStateData{} } -func (m *ConsensusStateData) String() string { return proto.CompactTextString(m) } -func (*ConsensusStateData) ProtoMessage() {} -func (*ConsensusStateData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{9} -} -func (m *ConsensusStateData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ConsensusStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ConsensusStateData.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 *ConsensusStateData) XXX_Merge(src proto.Message) { - xxx_messageInfo_ConsensusStateData.Merge(m, src) -} -func (m *ConsensusStateData) XXX_Size() int { - return m.Size() -} -func (m *ConsensusStateData) XXX_DiscardUnknown() { - xxx_messageInfo_ConsensusStateData.DiscardUnknown(m) -} - -var xxx_messageInfo_ConsensusStateData proto.InternalMessageInfo - -// ConnectionStateData returns the SignBytes data for connection state -// verification. -type ConnectionStateData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - Connection *types1.ConnectionEnd `protobuf:"bytes,2,opt,name=connection,proto3" json:"connection,omitempty"` -} - -func (m *ConnectionStateData) Reset() { *m = ConnectionStateData{} } -func (m *ConnectionStateData) String() string { return proto.CompactTextString(m) } -func (*ConnectionStateData) ProtoMessage() {} -func (*ConnectionStateData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{10} -} -func (m *ConnectionStateData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ConnectionStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ConnectionStateData.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 *ConnectionStateData) XXX_Merge(src proto.Message) { - xxx_messageInfo_ConnectionStateData.Merge(m, src) -} -func (m *ConnectionStateData) XXX_Size() int { - return m.Size() -} -func (m *ConnectionStateData) XXX_DiscardUnknown() { - xxx_messageInfo_ConnectionStateData.DiscardUnknown(m) -} - -var xxx_messageInfo_ConnectionStateData proto.InternalMessageInfo - -// ChannelStateData returns the SignBytes data for channel state -// verification. -type ChannelStateData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - Channel *types2.Channel `protobuf:"bytes,2,opt,name=channel,proto3" json:"channel,omitempty"` -} - -func (m *ChannelStateData) Reset() { *m = ChannelStateData{} } -func (m *ChannelStateData) String() string { return proto.CompactTextString(m) } -func (*ChannelStateData) ProtoMessage() {} -func (*ChannelStateData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{11} -} -func (m *ChannelStateData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ChannelStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ChannelStateData.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 *ChannelStateData) XXX_Merge(src proto.Message) { - xxx_messageInfo_ChannelStateData.Merge(m, src) -} -func (m *ChannelStateData) XXX_Size() int { - return m.Size() -} -func (m *ChannelStateData) XXX_DiscardUnknown() { - xxx_messageInfo_ChannelStateData.DiscardUnknown(m) -} - -var xxx_messageInfo_ChannelStateData proto.InternalMessageInfo - -// PacketCommitmentData returns the SignBytes data for packet commitment -// verification. -type PacketCommitmentData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - Commitment []byte `protobuf:"bytes,2,opt,name=commitment,proto3" json:"commitment,omitempty"` -} - -func (m *PacketCommitmentData) Reset() { *m = PacketCommitmentData{} } -func (m *PacketCommitmentData) String() string { return proto.CompactTextString(m) } -func (*PacketCommitmentData) ProtoMessage() {} -func (*PacketCommitmentData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{12} -} -func (m *PacketCommitmentData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PacketCommitmentData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PacketCommitmentData.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 *PacketCommitmentData) XXX_Merge(src proto.Message) { - xxx_messageInfo_PacketCommitmentData.Merge(m, src) -} -func (m *PacketCommitmentData) XXX_Size() int { - return m.Size() -} -func (m *PacketCommitmentData) XXX_DiscardUnknown() { - xxx_messageInfo_PacketCommitmentData.DiscardUnknown(m) -} - -var xxx_messageInfo_PacketCommitmentData proto.InternalMessageInfo - -func (m *PacketCommitmentData) GetPath() []byte { - if m != nil { - return m.Path - } - return nil -} - -func (m *PacketCommitmentData) GetCommitment() []byte { - if m != nil { - return m.Commitment - } - return nil -} - -// PacketAcknowledgementData returns the SignBytes data for acknowledgement -// verification. -type PacketAcknowledgementData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - Acknowledgement []byte `protobuf:"bytes,2,opt,name=acknowledgement,proto3" json:"acknowledgement,omitempty"` -} - -func (m *PacketAcknowledgementData) Reset() { *m = PacketAcknowledgementData{} } -func (m *PacketAcknowledgementData) String() string { return proto.CompactTextString(m) } -func (*PacketAcknowledgementData) ProtoMessage() {} -func (*PacketAcknowledgementData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{13} -} -func (m *PacketAcknowledgementData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PacketAcknowledgementData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PacketAcknowledgementData.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 *PacketAcknowledgementData) XXX_Merge(src proto.Message) { - xxx_messageInfo_PacketAcknowledgementData.Merge(m, src) -} -func (m *PacketAcknowledgementData) XXX_Size() int { - return m.Size() -} -func (m *PacketAcknowledgementData) XXX_DiscardUnknown() { - xxx_messageInfo_PacketAcknowledgementData.DiscardUnknown(m) -} - -var xxx_messageInfo_PacketAcknowledgementData proto.InternalMessageInfo - -func (m *PacketAcknowledgementData) GetPath() []byte { - if m != nil { - return m.Path - } - return nil -} - -func (m *PacketAcknowledgementData) GetAcknowledgement() []byte { - if m != nil { - return m.Acknowledgement - } - return nil -} - -// PacketReceiptAbsenceData returns the SignBytes data for -// packet receipt absence verification. -type PacketReceiptAbsenceData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` -} - -func (m *PacketReceiptAbsenceData) Reset() { *m = PacketReceiptAbsenceData{} } -func (m *PacketReceiptAbsenceData) String() string { return proto.CompactTextString(m) } -func (*PacketReceiptAbsenceData) ProtoMessage() {} -func (*PacketReceiptAbsenceData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{14} -} -func (m *PacketReceiptAbsenceData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PacketReceiptAbsenceData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PacketReceiptAbsenceData.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 *PacketReceiptAbsenceData) XXX_Merge(src proto.Message) { - xxx_messageInfo_PacketReceiptAbsenceData.Merge(m, src) -} -func (m *PacketReceiptAbsenceData) XXX_Size() int { - return m.Size() -} -func (m *PacketReceiptAbsenceData) XXX_DiscardUnknown() { - xxx_messageInfo_PacketReceiptAbsenceData.DiscardUnknown(m) -} - -var xxx_messageInfo_PacketReceiptAbsenceData proto.InternalMessageInfo - -func (m *PacketReceiptAbsenceData) GetPath() []byte { - if m != nil { - return m.Path - } - return nil -} - -// NextSequenceRecvData returns the SignBytes data for verification of the next -// sequence to be received. -type NextSequenceRecvData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - NextSeqRecv uint64 `protobuf:"varint,2,opt,name=next_seq_recv,json=nextSeqRecv,proto3" json:"next_seq_recv,omitempty" yaml:"next_seq_recv"` -} - -func (m *NextSequenceRecvData) Reset() { *m = NextSequenceRecvData{} } -func (m *NextSequenceRecvData) String() string { return proto.CompactTextString(m) } -func (*NextSequenceRecvData) ProtoMessage() {} -func (*NextSequenceRecvData) Descriptor() ([]byte, []int) { - return fileDescriptor_6cc2ee18f7f86d4e, []int{15} -} -func (m *NextSequenceRecvData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *NextSequenceRecvData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_NextSequenceRecvData.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 *NextSequenceRecvData) XXX_Merge(src proto.Message) { - xxx_messageInfo_NextSequenceRecvData.Merge(m, src) -} -func (m *NextSequenceRecvData) XXX_Size() int { - return m.Size() -} -func (m *NextSequenceRecvData) XXX_DiscardUnknown() { - xxx_messageInfo_NextSequenceRecvData.DiscardUnknown(m) -} - -var xxx_messageInfo_NextSequenceRecvData proto.InternalMessageInfo - -func (m *NextSequenceRecvData) GetPath() []byte { - if m != nil { - return m.Path - } - return nil -} - -func (m *NextSequenceRecvData) GetNextSeqRecv() uint64 { - if m != nil { - return m.NextSeqRecv - } - return 0 -} - -func init() { - proto.RegisterEnum("ibc.lightclients.solomachine.v1.DataType", DataType_name, DataType_value) - proto.RegisterType((*ClientState)(nil), "ibc.lightclients.solomachine.v1.ClientState") - proto.RegisterType((*ConsensusState)(nil), "ibc.lightclients.solomachine.v1.ConsensusState") - proto.RegisterType((*Header)(nil), "ibc.lightclients.solomachine.v1.Header") - proto.RegisterType((*Misbehaviour)(nil), "ibc.lightclients.solomachine.v1.Misbehaviour") - proto.RegisterType((*SignatureAndData)(nil), "ibc.lightclients.solomachine.v1.SignatureAndData") - proto.RegisterType((*TimestampedSignatureData)(nil), "ibc.lightclients.solomachine.v1.TimestampedSignatureData") - proto.RegisterType((*SignBytes)(nil), "ibc.lightclients.solomachine.v1.SignBytes") - proto.RegisterType((*HeaderData)(nil), "ibc.lightclients.solomachine.v1.HeaderData") - proto.RegisterType((*ClientStateData)(nil), "ibc.lightclients.solomachine.v1.ClientStateData") - proto.RegisterType((*ConsensusStateData)(nil), "ibc.lightclients.solomachine.v1.ConsensusStateData") - proto.RegisterType((*ConnectionStateData)(nil), "ibc.lightclients.solomachine.v1.ConnectionStateData") - proto.RegisterType((*ChannelStateData)(nil), "ibc.lightclients.solomachine.v1.ChannelStateData") - proto.RegisterType((*PacketCommitmentData)(nil), "ibc.lightclients.solomachine.v1.PacketCommitmentData") - proto.RegisterType((*PacketAcknowledgementData)(nil), "ibc.lightclients.solomachine.v1.PacketAcknowledgementData") - proto.RegisterType((*PacketReceiptAbsenceData)(nil), "ibc.lightclients.solomachine.v1.PacketReceiptAbsenceData") - proto.RegisterType((*NextSequenceRecvData)(nil), "ibc.lightclients.solomachine.v1.NextSequenceRecvData") -} - -func init() { - proto.RegisterFile("ibc/lightclients/solomachine/v1/solomachine.proto", fileDescriptor_6cc2ee18f7f86d4e) -} - -var fileDescriptor_6cc2ee18f7f86d4e = []byte{ - // 1372 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x5f, 0x8f, 0xdb, 0xd4, - 0x12, 0x5f, 0xa7, 0xe9, 0x76, 0x33, 0xd9, 0xee, 0xe6, 0xba, 0x69, 0x9b, 0x75, 0xab, 0xc4, 0xd7, - 0x57, 0xb7, 0x77, 0x2f, 0xa2, 0x71, 0x77, 0x11, 0x15, 0xaa, 0x50, 0xc1, 0x71, 0x0c, 0x4d, 0xbb, - 0xeb, 0x0d, 0x8e, 0x17, 0xda, 0x0a, 0xc9, 0x72, 0x9c, 0xb3, 0x59, 0xab, 0x89, 0x4f, 0x88, 0x9d, - 0xa4, 0x41, 0x42, 0x42, 0x3c, 0x95, 0x88, 0x07, 0xbe, 0x40, 0x24, 0x04, 0xe2, 0xab, 0x00, 0x8f, - 0xe5, 0x8d, 0xa7, 0x80, 0xda, 0x6f, 0x90, 0x4f, 0x80, 0xec, 0x73, 0x12, 0xdb, 0xd9, 0x6e, 0x56, - 0xfc, 0x7b, 0x3b, 0x67, 0xe6, 0x37, 0xbf, 0x99, 0x33, 0x33, 0x9e, 0x73, 0x0c, 0x3b, 0x76, 0xdd, - 0x12, 0x5b, 0x76, 0xf3, 0xd8, 0xb3, 0x5a, 0x36, 0x72, 0x3c, 0x57, 0x74, 0x71, 0x0b, 0xb7, 0x4d, - 0xeb, 0xd8, 0x76, 0x90, 0xd8, 0xdf, 0x89, 0x6e, 0x8b, 0x9d, 0x2e, 0xf6, 0x30, 0x5b, 0xb0, 0xeb, - 0x56, 0x31, 0x6a, 0x52, 0x8c, 0x62, 0xfa, 0x3b, 0xdc, 0xff, 0x7c, 0x4e, 0x0b, 0x77, 0x91, 0x68, - 0x61, 0xc7, 0x41, 0x96, 0x67, 0x63, 0xc7, 0xa7, 0x0a, 0x77, 0x84, 0x89, 0xfb, 0x77, 0x08, 0x3c, - 0x36, 0x1d, 0x07, 0xb5, 0x02, 0x14, 0x59, 0x52, 0x48, 0xb6, 0x89, 0x9b, 0x38, 0x58, 0x8a, 0xfe, - 0x8a, 0x4a, 0xb7, 0x9a, 0x18, 0x37, 0x5b, 0x48, 0x0c, 0x76, 0xf5, 0xde, 0x91, 0x68, 0x3a, 0x43, - 0xa2, 0x12, 0x7e, 0x4e, 0x40, 0x5a, 0x0e, 0xe2, 0xaa, 0x79, 0xa6, 0x87, 0x58, 0x0e, 0xd6, 0x5c, - 0xf4, 0x49, 0x0f, 0x39, 0x16, 0xca, 0x31, 0x3c, 0xb3, 0x9d, 0xd4, 0xe6, 0x7b, 0x56, 0x86, 0xcd, - 0xa3, 0x2e, 0xfe, 0x14, 0x39, 0xc6, 0x1c, 0x92, 0xf0, 0x21, 0x25, 0x6e, 0x3a, 0x29, 0x5c, 0x19, - 0x9a, 0xed, 0xd6, 0x1d, 0x61, 0x01, 0x20, 0x68, 0x1b, 0x44, 0x52, 0x9b, 0x91, 0x78, 0xb0, 0x69, - 0x61, 0xc7, 0x45, 0x8e, 0xdb, 0x73, 0x0d, 0xd7, 0xf7, 0x99, 0x3b, 0xc7, 0x33, 0xdb, 0xe9, 0x5d, - 0xb1, 0x78, 0x46, 0xa2, 0x8a, 0xf2, 0xcc, 0x2e, 0x08, 0x35, 0xea, 0x75, 0x81, 0x51, 0xd0, 0x36, - 0xac, 0x18, 0x96, 0x45, 0x70, 0xcd, 0x6c, 0xb5, 0xf0, 0xc0, 0xe8, 0x75, 0x1a, 0xa6, 0x87, 0x0c, - 0xf3, 0xc8, 0x43, 0x5d, 0xa3, 0xd3, 0xc5, 0x1d, 0xec, 0x9a, 0xad, 0x5c, 0x92, 0x67, 0xb6, 0xd7, - 0x4a, 0x37, 0xa6, 0x93, 0x82, 0x40, 0x08, 0x97, 0x80, 0x05, 0x2d, 0x17, 0x68, 0x0f, 0x03, 0xa5, - 0xe4, 0xeb, 0xaa, 0x54, 0x75, 0x27, 0xf9, 0xec, 0x9b, 0xc2, 0x8a, 0xf0, 0x2d, 0x03, 0x1b, 0xf1, - 0x58, 0xd9, 0xfb, 0x00, 0x9d, 0x5e, 0xbd, 0x65, 0x5b, 0xc6, 0x13, 0x34, 0x0c, 0x12, 0x9b, 0xde, - 0xcd, 0x16, 0x49, 0x59, 0x8a, 0xb3, 0xb2, 0x14, 0x25, 0x67, 0x58, 0xba, 0x3c, 0x9d, 0x14, 0xfe, - 0x45, 0x82, 0x08, 0x2d, 0x04, 0x2d, 0x45, 0x36, 0x0f, 0xd0, 0x90, 0xe5, 0x21, 0xdd, 0xb0, 0xfb, - 0xa8, 0xeb, 0xda, 0x47, 0x36, 0xea, 0x06, 0x25, 0x48, 0x69, 0x51, 0x11, 0x7b, 0x1d, 0x52, 0x9e, - 0xdd, 0x46, 0xae, 0x67, 0xb6, 0x3b, 0x41, 0x76, 0x93, 0x5a, 0x28, 0xa0, 0x41, 0x7e, 0x91, 0x80, - 0xd5, 0x7b, 0xc8, 0x6c, 0xa0, 0xee, 0xd2, 0x9a, 0xc7, 0xa8, 0x12, 0x0b, 0x54, 0xbe, 0xd6, 0xb5, - 0x9b, 0x8e, 0xe9, 0xf5, 0xba, 0xa4, 0x8c, 0xeb, 0x5a, 0x28, 0x60, 0x0f, 0x61, 0xc3, 0x41, 0x03, - 0x23, 0x72, 0xf0, 0xe4, 0x92, 0x83, 0x6f, 0x4d, 0x27, 0x85, 0xcb, 0xe4, 0xe0, 0x71, 0x2b, 0x41, - 0x5b, 0x77, 0xd0, 0xa0, 0x3a, 0x3f, 0xbf, 0x0c, 0x9b, 0x3e, 0x20, 0x9a, 0x83, 0xf3, 0x7e, 0x0e, - 0xa2, 0x0d, 0xb1, 0x00, 0x10, 0x34, 0x3f, 0x92, 0x72, 0x28, 0xa0, 0x49, 0xf8, 0x31, 0x01, 0xeb, - 0xfb, 0xb6, 0x5b, 0x47, 0xc7, 0x66, 0xdf, 0xc6, 0xbd, 0x2e, 0xbb, 0x03, 0x29, 0xd2, 0x7c, 0x86, - 0xdd, 0x08, 0x72, 0x91, 0x2a, 0x65, 0xa7, 0x93, 0x42, 0x86, 0xb6, 0xd9, 0x4c, 0x25, 0x68, 0x6b, - 0x64, 0x5d, 0x69, 0xc4, 0xb2, 0x97, 0x58, 0xc8, 0x5e, 0x07, 0x2e, 0xce, 0xd3, 0x61, 0x60, 0x67, - 0xd6, 0xea, 0x3b, 0x67, 0xb6, 0x7a, 0x6d, 0x66, 0x25, 0x39, 0x8d, 0xb2, 0xe9, 0x99, 0xa5, 0xdc, - 0x74, 0x52, 0xc8, 0x92, 0x28, 0x62, 0x8c, 0x82, 0xb6, 0x3e, 0xdf, 0x1f, 0x38, 0x0b, 0x1e, 0xbd, - 0x01, 0xa6, 0x29, 0xff, 0xbb, 0x3c, 0x7a, 0x03, 0x1c, 0xf5, 0xa8, 0x0f, 0x30, 0xcd, 0xe4, 0x0f, - 0x0c, 0x64, 0x16, 0x29, 0xe2, 0xed, 0xc1, 0x2c, 0xb6, 0xc7, 0xc7, 0x90, 0x6a, 0x98, 0x9e, 0x69, - 0x78, 0xc3, 0x0e, 0xc9, 0xdc, 0xc6, 0xee, 0xff, 0xcf, 0x0c, 0xd3, 0xe7, 0xd5, 0x87, 0x1d, 0x14, - 0x2d, 0xcb, 0x9c, 0x45, 0xd0, 0xd6, 0x1a, 0x54, 0xcf, 0xb2, 0x90, 0xf4, 0xd7, 0xb4, 0x2b, 0x83, - 0x75, 0xbc, 0x99, 0x93, 0xaf, 0xfe, 0x2e, 0x3e, 0x67, 0x20, 0xa7, 0xcf, 0x64, 0xa8, 0x31, 0x3f, - 0x53, 0x70, 0xa0, 0x77, 0x61, 0x23, 0xcc, 0x45, 0x40, 0x1f, 0x9c, 0x2a, 0xda, 0xbb, 0x71, 0xbd, - 0xa0, 0x85, 0xe5, 0x28, 0x9f, 0x08, 0x21, 0xf1, 0xea, 0x10, 0x7e, 0x65, 0x20, 0xe5, 0xfb, 0x2d, - 0x0d, 0x3d, 0xe4, 0xfe, 0x85, 0xaf, 0x73, 0x61, 0x50, 0x9c, 0x3b, 0x39, 0x28, 0x62, 0x25, 0x48, - 0xfe, 0x53, 0x25, 0x38, 0x1f, 0x96, 0x80, 0x9e, 0xf0, 0x7b, 0x06, 0x80, 0x0c, 0x9f, 0x20, 0x29, - 0x7b, 0x90, 0xa6, 0x9f, 0xfc, 0x99, 0xe3, 0xf1, 0xca, 0x74, 0x52, 0x60, 0x63, 0x53, 0x82, 0xce, - 0x47, 0x32, 0x22, 0x4e, 0x99, 0x0f, 0x89, 0x3f, 0x39, 0x1f, 0x3e, 0x83, 0xcd, 0xc8, 0xe5, 0x18, - 0xc4, 0xca, 0x42, 0xb2, 0x63, 0x7a, 0xc7, 0xb4, 0x9d, 0x83, 0x35, 0x5b, 0x85, 0x75, 0x3a, 0x1a, - 0xc8, 0x85, 0x96, 0x58, 0x72, 0x80, 0xab, 0xd3, 0x49, 0xe1, 0x52, 0x6c, 0x9c, 0xd0, 0x2b, 0x2b, - 0x6d, 0x85, 0x9e, 0xa8, 0xfb, 0x2f, 0x19, 0x60, 0xe3, 0x17, 0xc9, 0xa9, 0x21, 0x3c, 0x3a, 0x79, - 0xad, 0x2e, 0x8b, 0xe2, 0x0f, 0xdc, 0x9d, 0x34, 0x96, 0x3e, 0x5c, 0x92, 0xe7, 0x0f, 0x92, 0xe5, - 0xb1, 0x28, 0x00, 0xe1, 0xdb, 0x85, 0x86, 0xf1, 0xdf, 0xa0, 0xad, 0xfc, 0xc7, 0x4b, 0x31, 0xf2, - 0xae, 0x21, 0x97, 0x3a, 0xdd, 0x29, 0x4e, 0x43, 0x8b, 0x18, 0x52, 0xbf, 0x0d, 0xc8, 0xc8, 0xe4, - 0x89, 0xb3, 0xdc, 0xe9, 0x6d, 0xb8, 0x40, 0x9f, 0x42, 0xd4, 0xe3, 0xf5, 0x88, 0x47, 0xfa, 0x46, - 0xf2, 0xdd, 0x91, 0xa5, 0x36, 0x03, 0x53, 0x2f, 0xf7, 0x21, 0x5b, 0x35, 0xad, 0x27, 0xc8, 0x93, - 0x71, 0xbb, 0x6d, 0x7b, 0x6d, 0xe4, 0x78, 0xa7, 0x7a, 0xca, 0xfb, 0xc7, 0x9b, 0xa1, 0x02, 0x67, - 0xeb, 0x5a, 0x44, 0x22, 0x3c, 0x82, 0x2d, 0xc2, 0x25, 0x59, 0x4f, 0x1c, 0x3c, 0x68, 0xa1, 0x46, - 0x13, 0x2d, 0x25, 0xdc, 0x86, 0x4d, 0x33, 0x0e, 0xa5, 0xac, 0x8b, 0x62, 0xa1, 0x08, 0x39, 0x42, - 0xad, 0x21, 0x0b, 0xd9, 0x1d, 0x4f, 0xaa, 0xbb, 0xfe, 0x1c, 0x38, 0x8d, 0x59, 0x38, 0x86, 0xac, - 0x8a, 0x9e, 0x7a, 0xb3, 0xc7, 0x97, 0x86, 0xac, 0xfe, 0xa9, 0x51, 0xbc, 0x0d, 0x17, 0x1d, 0xf4, - 0xd4, 0xf3, 0x9f, 0x6e, 0x46, 0x17, 0x59, 0x7d, 0xfa, 0xb6, 0x8b, 0x5c, 0x03, 0x31, 0xb5, 0xa0, - 0xa5, 0x1d, 0x42, 0xed, 0xb3, 0xbe, 0xf6, 0x55, 0x12, 0xd6, 0x66, 0x83, 0x81, 0x7d, 0x0b, 0xfe, - 0x53, 0x96, 0x74, 0xc9, 0xd0, 0x1f, 0x55, 0x15, 0xe3, 0x50, 0xad, 0xa8, 0x15, 0xbd, 0x22, 0xed, - 0x55, 0x1e, 0x2b, 0x65, 0xe3, 0x50, 0xad, 0x55, 0x15, 0xb9, 0xf2, 0x5e, 0x45, 0x29, 0x67, 0x56, - 0xb8, 0xcd, 0xd1, 0x98, 0x4f, 0x47, 0x44, 0xec, 0x0d, 0xb8, 0x12, 0x5a, 0xca, 0x7b, 0x15, 0x45, - 0xd5, 0x8d, 0x9a, 0x2e, 0xe9, 0x4a, 0x86, 0xe1, 0x60, 0x34, 0xe6, 0x57, 0x89, 0x8c, 0x7d, 0x1d, - 0xb6, 0x22, 0xb8, 0x03, 0xb5, 0xa6, 0xa8, 0xb5, 0xc3, 0x1a, 0x85, 0x26, 0xb8, 0x8b, 0xa3, 0x31, - 0x9f, 0x9a, 0x8b, 0xd9, 0x22, 0x70, 0x31, 0xb4, 0xaa, 0xc8, 0x7a, 0xe5, 0x40, 0xa5, 0xf0, 0x73, - 0xdc, 0xc6, 0x68, 0xcc, 0x43, 0x28, 0x67, 0xb7, 0xe1, 0x6a, 0x04, 0x7f, 0x4f, 0x52, 0x55, 0x65, - 0x8f, 0x82, 0x93, 0x5c, 0x7a, 0x34, 0xe6, 0x2f, 0x50, 0x21, 0xfb, 0x26, 0x5c, 0x0b, 0x91, 0x55, - 0x49, 0x7e, 0xa0, 0xe8, 0x86, 0x7c, 0xb0, 0xbf, 0x5f, 0xd1, 0xf7, 0x15, 0x55, 0xcf, 0x9c, 0xe7, - 0xb2, 0xa3, 0x31, 0x9f, 0x21, 0x8a, 0x50, 0xce, 0xbe, 0x03, 0xfc, 0x09, 0x33, 0x49, 0x7e, 0xa0, - 0x1e, 0x7c, 0xb4, 0xa7, 0x94, 0xdf, 0x57, 0x02, 0xdb, 0x55, 0x6e, 0x6b, 0x34, 0xe6, 0x2f, 0x13, - 0xed, 0x82, 0x92, 0xbd, 0xfb, 0x0a, 0x02, 0x4d, 0x91, 0x95, 0x4a, 0x55, 0x37, 0xa4, 0x52, 0x4d, - 0x51, 0x65, 0x25, 0x73, 0x81, 0xcb, 0x8d, 0xc6, 0x7c, 0x96, 0x68, 0xa9, 0x92, 0xea, 0xd8, 0xdb, - 0x70, 0x3d, 0xb4, 0x57, 0x95, 0x87, 0xba, 0x51, 0x53, 0x3e, 0x38, 0xf4, 0x55, 0x3e, 0xcd, 0x87, - 0x99, 0x35, 0x12, 0xb8, 0xaf, 0x99, 0x29, 0x7c, 0x39, 0xcb, 0x43, 0x26, 0xb4, 0xbb, 0xa7, 0x48, - 0x65, 0x45, 0xcb, 0xa4, 0x48, 0x65, 0xc8, 0x8e, 0x4b, 0x3e, 0xfb, 0x2e, 0xbf, 0x52, 0x7a, 0xf8, - 0xd3, 0x8b, 0x3c, 0xf3, 0xfc, 0x45, 0x9e, 0xf9, 0xed, 0x45, 0x9e, 0xf9, 0xfa, 0x65, 0x7e, 0xe5, - 0xf9, 0xcb, 0xfc, 0xca, 0x2f, 0x2f, 0xf3, 0x2b, 0x8f, 0xef, 0x36, 0x6d, 0xef, 0xb8, 0x57, 0x2f, - 0x5a, 0xb8, 0x2d, 0x5a, 0xd8, 0x6d, 0x63, 0x57, 0xb4, 0xeb, 0xd6, 0xcd, 0x26, 0x16, 0xfb, 0xb7, - 0xc5, 0x36, 0x6e, 0xf4, 0x5a, 0xc8, 0x25, 0x3f, 0x39, 0xb7, 0x76, 0x6f, 0x92, 0x91, 0x28, 0xb6, - 0x50, 0xd3, 0xb4, 0x86, 0x62, 0x7f, 0xe7, 0xd6, 0xad, 0xfa, 0x6a, 0x30, 0xc7, 0xde, 0xf8, 0x3d, - 0x00, 0x00, 0xff, 0xff, 0xa2, 0xf0, 0xd7, 0x92, 0x8a, 0x0d, 0x00, 0x00, -} - -func (m *ClientState) 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 *ClientState) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ClientState) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.AllowUpdateAfterProposal { - i-- - if m.AllowUpdateAfterProposal { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x20 - } - if m.ConsensusState != nil { - { - size, err := m.ConsensusState.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a - } - if m.FrozenSequence != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.FrozenSequence)) - i-- - dAtA[i] = 0x10 - } - if m.Sequence != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func (m *ConsensusState) 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 *ConsensusState) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ConsensusState) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Timestamp != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) - i-- - dAtA[i] = 0x18 - } - if len(m.Diversifier) > 0 { - i -= len(m.Diversifier) - copy(dAtA[i:], m.Diversifier) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Diversifier))) - i-- - dAtA[i] = 0x12 - } - if m.PublicKey != nil { - { - size, err := m.PublicKey.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *Header) 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 *Header) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *Header) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.NewDiversifier) > 0 { - i -= len(m.NewDiversifier) - copy(dAtA[i:], m.NewDiversifier) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.NewDiversifier))) - i-- - dAtA[i] = 0x2a - } - if m.NewPublicKey != nil { - { - size, err := m.NewPublicKey.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x22 - } - if len(m.Signature) > 0 { - i -= len(m.Signature) - copy(dAtA[i:], m.Signature) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Signature))) - i-- - dAtA[i] = 0x1a - } - if m.Timestamp != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) - i-- - dAtA[i] = 0x10 - } - if m.Sequence != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func (m *Misbehaviour) 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 *Misbehaviour) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *Misbehaviour) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.SignatureTwo != nil { - { - size, err := m.SignatureTwo.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x22 - } - if m.SignatureOne != nil { - { - size, err := m.SignatureOne.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a - } - if m.Sequence != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) - i-- - dAtA[i] = 0x10 - } - if len(m.ClientId) > 0 { - i -= len(m.ClientId) - copy(dAtA[i:], m.ClientId) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.ClientId))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *SignatureAndData) 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 *SignatureAndData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *SignatureAndData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Timestamp != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) - i-- - dAtA[i] = 0x20 - } - if len(m.Data) > 0 { - i -= len(m.Data) - copy(dAtA[i:], m.Data) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Data))) - i-- - dAtA[i] = 0x1a - } - if m.DataType != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.DataType)) - i-- - dAtA[i] = 0x10 - } - if len(m.Signature) > 0 { - i -= len(m.Signature) - copy(dAtA[i:], m.Signature) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Signature))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *TimestampedSignatureData) 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 *TimestampedSignatureData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *TimestampedSignatureData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Timestamp != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) - i-- - dAtA[i] = 0x10 - } - if len(m.SignatureData) > 0 { - i -= len(m.SignatureData) - copy(dAtA[i:], m.SignatureData) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.SignatureData))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *SignBytes) 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 *SignBytes) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *SignBytes) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Data) > 0 { - i -= len(m.Data) - copy(dAtA[i:], m.Data) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Data))) - i-- - dAtA[i] = 0x2a - } - if m.DataType != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.DataType)) - i-- - dAtA[i] = 0x20 - } - if len(m.Diversifier) > 0 { - i -= len(m.Diversifier) - copy(dAtA[i:], m.Diversifier) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Diversifier))) - i-- - dAtA[i] = 0x1a - } - if m.Timestamp != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) - i-- - dAtA[i] = 0x10 - } - if m.Sequence != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func (m *HeaderData) 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 *HeaderData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *HeaderData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.NewDiversifier) > 0 { - i -= len(m.NewDiversifier) - copy(dAtA[i:], m.NewDiversifier) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.NewDiversifier))) - i-- - dAtA[i] = 0x12 - } - if m.NewPubKey != nil { - { - size, err := m.NewPubKey.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *ClientStateData) 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 *ClientStateData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ClientStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.ClientState != nil { - { - size, err := m.ClientState.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *ConsensusStateData) 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 *ConsensusStateData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ConsensusStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.ConsensusState != nil { - { - size, err := m.ConsensusState.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *ConnectionStateData) 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 *ConnectionStateData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ConnectionStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Connection != nil { - { - size, err := m.Connection.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *ChannelStateData) 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 *ChannelStateData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ChannelStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Channel != nil { - { - size, err := m.Channel.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *PacketCommitmentData) 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 *PacketCommitmentData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PacketCommitmentData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Commitment) > 0 { - i -= len(m.Commitment) - copy(dAtA[i:], m.Commitment) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Commitment))) - i-- - dAtA[i] = 0x12 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *PacketAcknowledgementData) 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 *PacketAcknowledgementData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PacketAcknowledgementData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Acknowledgement) > 0 { - i -= len(m.Acknowledgement) - copy(dAtA[i:], m.Acknowledgement) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Acknowledgement))) - i-- - dAtA[i] = 0x12 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *PacketReceiptAbsenceData) 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 *PacketReceiptAbsenceData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PacketReceiptAbsenceData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *NextSequenceRecvData) 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 *NextSequenceRecvData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *NextSequenceRecvData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.NextSeqRecv != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.NextSeqRecv)) - i-- - dAtA[i] = 0x10 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func encodeVarintSolomachine(dAtA []byte, offset int, v uint64) int { - offset -= sovSolomachine(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *ClientState) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Sequence != 0 { - n += 1 + sovSolomachine(uint64(m.Sequence)) - } - if m.FrozenSequence != 0 { - n += 1 + sovSolomachine(uint64(m.FrozenSequence)) - } - if m.ConsensusState != nil { - l = m.ConsensusState.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.AllowUpdateAfterProposal { - n += 2 - } - return n -} - -func (m *ConsensusState) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.PublicKey != nil { - l = m.PublicKey.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - l = len(m.Diversifier) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Timestamp != 0 { - n += 1 + sovSolomachine(uint64(m.Timestamp)) - } - return n -} - -func (m *Header) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Sequence != 0 { - n += 1 + sovSolomachine(uint64(m.Sequence)) - } - if m.Timestamp != 0 { - n += 1 + sovSolomachine(uint64(m.Timestamp)) - } - l = len(m.Signature) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.NewPublicKey != nil { - l = m.NewPublicKey.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - l = len(m.NewDiversifier) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *Misbehaviour) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ClientId) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Sequence != 0 { - n += 1 + sovSolomachine(uint64(m.Sequence)) - } - if m.SignatureOne != nil { - l = m.SignatureOne.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.SignatureTwo != nil { - l = m.SignatureTwo.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *SignatureAndData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Signature) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.DataType != 0 { - n += 1 + sovSolomachine(uint64(m.DataType)) - } - l = len(m.Data) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Timestamp != 0 { - n += 1 + sovSolomachine(uint64(m.Timestamp)) - } - return n -} - -func (m *TimestampedSignatureData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.SignatureData) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Timestamp != 0 { - n += 1 + sovSolomachine(uint64(m.Timestamp)) - } - return n -} - -func (m *SignBytes) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Sequence != 0 { - n += 1 + sovSolomachine(uint64(m.Sequence)) - } - if m.Timestamp != 0 { - n += 1 + sovSolomachine(uint64(m.Timestamp)) - } - l = len(m.Diversifier) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.DataType != 0 { - n += 1 + sovSolomachine(uint64(m.DataType)) - } - l = len(m.Data) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *HeaderData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.NewPubKey != nil { - l = m.NewPubKey.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - l = len(m.NewDiversifier) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *ClientStateData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.ClientState != nil { - l = m.ClientState.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *ConsensusStateData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.ConsensusState != nil { - l = m.ConsensusState.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *ConnectionStateData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Connection != nil { - l = m.Connection.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *ChannelStateData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Channel != nil { - l = m.Channel.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *PacketCommitmentData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - l = len(m.Commitment) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *PacketAcknowledgementData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - l = len(m.Acknowledgement) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *PacketReceiptAbsenceData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *NextSequenceRecvData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.NextSeqRecv != 0 { - n += 1 + sovSolomachine(uint64(m.NextSeqRecv)) - } - return n -} - -func sovSolomachine(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozSolomachine(x uint64) (n int) { - return sovSolomachine(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *ClientState) 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 ErrIntOverflowSolomachine - } - 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: ClientState: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ClientState: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) - } - m.Sequence = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Sequence |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field FrozenSequence", wireType) - } - m.FrozenSequence = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.FrozenSequence |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.ConsensusState == nil { - m.ConsensusState = &ConsensusState{} - } - if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field AllowUpdateAfterProposal", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.AllowUpdateAfterProposal = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ConsensusState) 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 ErrIntOverflowSolomachine - } - 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: ConsensusState: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ConsensusState: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.PublicKey == nil { - m.PublicKey = &types.Any{} - } - if err := m.PublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Diversifier", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - 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 ErrInvalidLengthSolomachine - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Diversifier = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) - } - m.Timestamp = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Timestamp |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *Header) 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 ErrIntOverflowSolomachine - } - 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: Header: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Header: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) - } - m.Sequence = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Sequence |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) - } - m.Timestamp = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Timestamp |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) - if m.Signature == nil { - m.Signature = []byte{} - } - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NewPublicKey", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.NewPublicKey == nil { - m.NewPublicKey = &types.Any{} - } - if err := m.NewPublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NewDiversifier", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - 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 ErrInvalidLengthSolomachine - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.NewDiversifier = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *Misbehaviour) 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 ErrIntOverflowSolomachine - } - 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: Misbehaviour: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Misbehaviour: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - 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 ErrInvalidLengthSolomachine - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ClientId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) - } - m.Sequence = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Sequence |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SignatureOne", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.SignatureOne == nil { - m.SignatureOne = &SignatureAndData{} - } - if err := m.SignatureOne.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SignatureTwo", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.SignatureTwo == nil { - m.SignatureTwo = &SignatureAndData{} - } - if err := m.SignatureTwo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *SignatureAndData) 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 ErrIntOverflowSolomachine - } - 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: SignatureAndData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: SignatureAndData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) - if m.Signature == nil { - m.Signature = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field DataType", wireType) - } - m.DataType = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.DataType |= DataType(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) - if m.Data == nil { - m.Data = []byte{} - } - iNdEx = postIndex - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) - } - m.Timestamp = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Timestamp |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *TimestampedSignatureData) 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 ErrIntOverflowSolomachine - } - 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: TimestampedSignatureData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TimestampedSignatureData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SignatureData", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.SignatureData = append(m.SignatureData[:0], dAtA[iNdEx:postIndex]...) - if m.SignatureData == nil { - m.SignatureData = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) - } - m.Timestamp = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Timestamp |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *SignBytes) 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 ErrIntOverflowSolomachine - } - 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: SignBytes: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: SignBytes: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) - } - m.Sequence = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Sequence |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) - } - m.Timestamp = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Timestamp |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Diversifier", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - 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 ErrInvalidLengthSolomachine - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Diversifier = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field DataType", wireType) - } - m.DataType = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.DataType |= DataType(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) - if m.Data == nil { - m.Data = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *HeaderData) 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 ErrIntOverflowSolomachine - } - 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: HeaderData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: HeaderData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NewPubKey", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.NewPubKey == nil { - m.NewPubKey = &types.Any{} - } - if err := m.NewPubKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NewDiversifier", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - 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 ErrInvalidLengthSolomachine - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.NewDiversifier = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ClientStateData) 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 ErrIntOverflowSolomachine - } - 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: ClientStateData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ClientStateData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClientState", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.ClientState == nil { - m.ClientState = &types.Any{} - } - if err := m.ClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ConsensusStateData) 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 ErrIntOverflowSolomachine - } - 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: ConsensusStateData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ConsensusStateData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.ConsensusState == nil { - m.ConsensusState = &types.Any{} - } - if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ConnectionStateData) 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 ErrIntOverflowSolomachine - } - 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: ConnectionStateData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ConnectionStateData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Connection", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Connection == nil { - m.Connection = &types1.ConnectionEnd{} - } - if err := m.Connection.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ChannelStateData) 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 ErrIntOverflowSolomachine - } - 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: ChannelStateData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ChannelStateData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Channel", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Channel == nil { - m.Channel = &types2.Channel{} - } - if err := m.Channel.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PacketCommitmentData) 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 ErrIntOverflowSolomachine - } - 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: PacketCommitmentData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: PacketCommitmentData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Commitment", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Commitment = append(m.Commitment[:0], dAtA[iNdEx:postIndex]...) - if m.Commitment == nil { - m.Commitment = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PacketAcknowledgementData) 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 ErrIntOverflowSolomachine - } - 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: PacketAcknowledgementData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: PacketAcknowledgementData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Acknowledgement", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Acknowledgement = append(m.Acknowledgement[:0], dAtA[iNdEx:postIndex]...) - if m.Acknowledgement == nil { - m.Acknowledgement = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PacketReceiptAbsenceData) 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 ErrIntOverflowSolomachine - } - 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: PacketReceiptAbsenceData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: PacketReceiptAbsenceData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *NextSequenceRecvData) 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 ErrIntOverflowSolomachine - } - 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: NextSequenceRecvData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: NextSequenceRecvData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field NextSeqRecv", wireType) - } - m.NextSeqRecv = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.NextSeqRecv |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipSolomachine(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowSolomachine - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowSolomachine - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowSolomachine - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthSolomachine - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupSolomachine - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthSolomachine - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLengthSolomachine = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowSolomachine = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupSolomachine = fmt.Errorf("proto: unexpected end of group") -) diff --git a/modules/core/02-client/legacy/v100/store.go b/modules/core/02-client/legacy/v100/store.go deleted file mode 100644 index a7bac3529f5..00000000000 --- a/modules/core/02-client/legacy/v100/store.go +++ /dev/null @@ -1,172 +0,0 @@ -package v100 - -import ( - "fmt" - "strings" - - "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/store/prefix" - storetypes "github.com/cosmos/cosmos-sdk/store/types" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v6/modules/core/24-host" - "github.com/cosmos/ibc-go/v6/modules/core/exported" - solomachine "github.com/cosmos/ibc-go/v6/modules/light-clients/06-solomachine" - ibctm "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint" -) - -// MigrateStore performs in-place store migrations from SDK v0.40 of the IBC module to v1.0.0 of ibc-go. -// The migration includes: -// -// - Migrating solo machine client states from v1 to v2 protobuf definition -// - Pruning all solo machine consensus states -// - Pruning expired tendermint consensus states -// - Adds ProcessedHeight and Iteration keys for unexpired tendermint consensus states -func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.BinaryCodec) (err error) { - store := ctx.KVStore(storeKey) - iterator := sdk.KVStorePrefixIterator(store, host.KeyClientStorePrefix) - - var clients []string - - // collect all clients - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - keySplit := strings.Split(string(iterator.Key()), "/") - if keySplit[len(keySplit)-1] != host.KeyClientState { - continue - } - - // key is clients/{clientid}/clientState - // Thus, keySplit[1] is clientID - clients = append(clients, keySplit[1]) - } - - for _, clientID := range clients { - clientType, _, err := clienttypes.ParseClientIdentifier(clientID) - if err != nil { - return err - } - - clientPrefix := []byte(fmt.Sprintf("%s/%s/", host.KeyClientStorePrefix, clientID)) - clientStore := prefix.NewStore(ctx.KVStore(storeKey), clientPrefix) - - bz := clientStore.Get(host.ClientStateKey()) - if bz == nil { - return clienttypes.ErrClientNotFound - } - - switch clientType { - case exported.Solomachine: - any := &codectypes.Any{} - if err := cdc.Unmarshal(bz, any); err != nil { - return sdkerrors.Wrap(err, "failed to unmarshal client state bytes into solo machine client state") - } - - clientState := &ClientState{} - if err := cdc.Unmarshal(any.Value, clientState); err != nil { - return sdkerrors.Wrap(err, "failed to unmarshal client state bytes into solo machine client state") - } - - updatedClientState := migrateSolomachine(clientState) - - bz, err := clienttypes.MarshalClientState(cdc, updatedClientState) - if err != nil { - return sdkerrors.Wrap(err, "failed to unmarshal client state bytes into solo machine client state") - } - - // update solomachine in store - clientStore.Set(host.ClientStateKey(), bz) - - pruneSolomachineConsensusStates(clientStore) - - case exported.Tendermint: - var clientState exported.ClientState - if err := cdc.UnmarshalInterface(bz, &clientState); err != nil { - return sdkerrors.Wrap(err, "failed to unmarshal client state bytes into tendermint client state") - } - - tmClientState, ok := clientState.(*ibctm.ClientState) - if !ok { - return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "client state is not tendermint even though client id contains 07-tendermint") - } - - // add iteration keys so pruning will be successful - addConsensusMetadata(ctx, clientStore) - - ibctm.PruneAllExpiredConsensusStates(ctx, clientStore, cdc, tmClientState) - default: - continue - } - } - - return nil -} - -// migrateSolomachine migrates the solomachine from v1 to v2 solo machine protobuf definition. -func migrateSolomachine(clientState *ClientState) *solomachine.ClientState { - isFrozen := clientState.FrozenSequence != 0 - consensusState := &solomachine.ConsensusState{ - PublicKey: clientState.ConsensusState.PublicKey, - Diversifier: clientState.ConsensusState.Diversifier, - Timestamp: clientState.ConsensusState.Timestamp, - } - - return &solomachine.ClientState{ - Sequence: clientState.Sequence, - IsFrozen: isFrozen, - ConsensusState: consensusState, - } -} - -// pruneSolomachineConsensusStates removes all solomachine consensus states from the -// client store. -func pruneSolomachineConsensusStates(clientStore sdk.KVStore) { - iterator := sdk.KVStorePrefixIterator(clientStore, []byte(host.KeyConsensusStatePrefix)) - var heights []exported.Height - - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - keySplit := strings.Split(string(iterator.Key()), "/") - // key is in the format "consensusStates/" - if len(keySplit) != 2 || keySplit[0] != string(host.KeyConsensusStatePrefix) { - continue - } - - // collect consensus states to be pruned - heights = append(heights, clienttypes.MustParseHeight(keySplit[1])) - } - - // delete all consensus states - for _, height := range heights { - clientStore.Delete(host.ConsensusStateKey(height)) - } -} - -// addConsensusMetadata adds the iteration key and processed height for all tendermint consensus states -// These keys were not included in the previous release of the IBC module. Adding the iteration keys allows -// for pruning iteration. -func addConsensusMetadata(ctx sdk.Context, clientStore sdk.KVStore) { - var heights []exported.Height - iterator := sdk.KVStorePrefixIterator(clientStore, []byte(host.KeyConsensusStatePrefix)) - - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - keySplit := strings.Split(string(iterator.Key()), "/") - // consensus key is in the format "consensusStates/" - if len(keySplit) != 2 { - continue - } - - heights = append(heights, clienttypes.MustParseHeight(keySplit[1])) - } - - for _, height := range heights { - // set the iteration key and processed height - // these keys were not included in the SDK v0.42.0 release - ibctm.SetProcessedHeight(clientStore, height, clienttypes.GetSelfHeight(ctx)) - ibctm.SetIterationKey(clientStore, height) - } -} diff --git a/modules/core/02-client/legacy/v100/store_test.go b/modules/core/02-client/legacy/v100/store_test.go deleted file mode 100644 index db0c7043310..00000000000 --- a/modules/core/02-client/legacy/v100/store_test.go +++ /dev/null @@ -1,230 +0,0 @@ -package v100_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - - v100 "github.com/cosmos/ibc-go/v6/modules/core/02-client/legacy/v100" - "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v6/modules/core/24-host" - "github.com/cosmos/ibc-go/v6/modules/core/exported" - ibctm "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v6/testing" -) - -type LegacyTestSuite struct { - suite.Suite - - coordinator *ibctesting.Coordinator - - // testing chains used for convenience and readability - chainA *ibctesting.TestChain - chainB *ibctesting.TestChain -} - -// TestLegacyTestSuite runs all the tests within this package. -func TestLegacyTestSuite(t *testing.T) { - suite.Run(t, new(LegacyTestSuite)) -} - -// SetupTest creates a coordinator with 2 test chains. -func (suite *LegacyTestSuite) SetupTest() { - suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) - suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) - suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) - // commit some blocks so that QueryProof returns valid proof (cannot return valid query if height <= 1) - suite.coordinator.CommitNBlocks(suite.chainA, 2) - suite.coordinator.CommitNBlocks(suite.chainB, 2) -} - -// only test migration for solo machines -// ensure all client states are migrated and all consensus states -// are removed -func (suite *LegacyTestSuite) TestMigrateStoreSolomachine() { - path := ibctesting.NewPath(suite.chainA, suite.chainB) - - // create multiple legacy solo machine clients - solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1) - solomachineMulti := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4) - - // manually generate old proto buf definitions and set in store - // NOTE: we cannot use 'CreateClient' and 'UpdateClient' functions since we are - // using client states and consensus states which do not implement the exported.ClientState - // and exported.ConsensusState interface - for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} { - clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(path.EndpointA.Chain.GetContext(), sm.ClientID) - clientState := sm.ClientState() - - var seq uint64 - if clientState.IsFrozen { - seq = 1 - } - - // generate old client state proto definition - legacyClientState := &v100.ClientState{ - Sequence: clientState.Sequence, - FrozenSequence: seq, - ConsensusState: &v100.ConsensusState{ - PublicKey: clientState.ConsensusState.PublicKey, - Diversifier: clientState.ConsensusState.Diversifier, - Timestamp: clientState.ConsensusState.Timestamp, - }, - } - - // set client state - bz, err := path.EndpointA.Chain.App.AppCodec().MarshalInterface(legacyClientState) - suite.Require().NoError(err) - clientStore.Set(host.ClientStateKey(), bz) - - // set some consensus states - height1 := types.NewHeight(0, 1) - height2 := types.NewHeight(1, 2) - height3 := types.NewHeight(0, 123) - - bz, err = path.EndpointA.Chain.App.AppCodec().MarshalInterface(legacyClientState.ConsensusState) - suite.Require().NoError(err) - clientStore.Set(host.ConsensusStateKey(height1), bz) - clientStore.Set(host.ConsensusStateKey(height2), bz) - clientStore.Set(host.ConsensusStateKey(height3), bz) - } - - // create tendermint clients - suite.coordinator.SetupClients(path) - - err := v100.MigrateStore(path.EndpointA.Chain.GetContext(), path.EndpointA.Chain.GetSimApp().GetKey(host.StoreKey), path.EndpointA.Chain.App.AppCodec()) - suite.Require().NoError(err) - - // verify client state has been migrated - for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} { - clientState, ok := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.GetClientState(path.EndpointA.Chain.GetContext(), sm.ClientID) - suite.Require().True(ok) - suite.Require().Equal(sm.ClientState(), clientState) - } - - // verify consensus states have been removed - for _, sm := range []*ibctesting.Solomachine{solomachine, solomachineMulti} { - clientConsensusStates := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.GetAllConsensusStates(path.EndpointA.Chain.GetContext()) - for _, client := range clientConsensusStates { - // GetAllConsensusStates should not return consensus states for our solo machine clients - suite.Require().NotEqual(sm.ClientID, client.ClientId) - } - } -} - -// only test migration for tendermint clients -// ensure all expired consensus states are removed from tendermint client stores -func (suite *LegacyTestSuite) TestMigrateStoreTendermint() { - // create path and setup clients - path1 := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.SetupClients(path1) - - path2 := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.SetupClients(path2) - pruneHeightMap := make(map[*ibctesting.Path][]exported.Height) - unexpiredHeightMap := make(map[*ibctesting.Path][]exported.Height) - - for _, path := range []*ibctesting.Path{path1, path2} { - // collect all heights expected to be pruned - var pruneHeights []exported.Height - pruneHeights = append(pruneHeights, path.EndpointA.GetClientState().GetLatestHeight()) - - // these heights will be expired and also pruned - for i := 0; i < 3; i++ { - path.EndpointA.UpdateClient() - pruneHeights = append(pruneHeights, path.EndpointA.GetClientState().GetLatestHeight()) - } - - // double chedck all information is currently stored - for _, pruneHeight := range pruneHeights { - consState, ok := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, pruneHeight) - suite.Require().True(ok) - suite.Require().NotNil(consState) - - ctx := path.EndpointA.Chain.GetContext() - clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - - processedTime, ok := ibctm.GetProcessedTime(clientStore, pruneHeight) - suite.Require().True(ok) - suite.Require().NotNil(processedTime) - - processedHeight, ok := ibctm.GetProcessedHeight(clientStore, pruneHeight) - suite.Require().True(ok) - suite.Require().NotNil(processedHeight) - - expectedConsKey := ibctm.GetIterationKey(clientStore, pruneHeight) - suite.Require().NotNil(expectedConsKey) - } - pruneHeightMap[path] = pruneHeights - } - - // Increment the time by a week - suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) - - for _, path := range []*ibctesting.Path{path1, path2} { - // create the consensus state that can be used as trusted height for next update - var unexpiredHeights []exported.Height - path.EndpointA.UpdateClient() - unexpiredHeights = append(unexpiredHeights, path.EndpointA.GetClientState().GetLatestHeight()) - path.EndpointA.UpdateClient() - unexpiredHeights = append(unexpiredHeights, path.EndpointA.GetClientState().GetLatestHeight()) - - // remove processed height and iteration keys since these were missing from previous version of ibc module - clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(path.EndpointA.Chain.GetContext(), path.EndpointA.ClientID) - for _, height := range unexpiredHeights { - clientStore.Delete(ibctm.ProcessedHeightKey(height)) - clientStore.Delete(ibctm.IterationKey(height)) - } - - unexpiredHeightMap[path] = unexpiredHeights - } - - // Increment the time by another week, then update the client. - // This will cause the consensus states created before the first time increment - // to be expired - suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) - err := v100.MigrateStore(path1.EndpointA.Chain.GetContext(), path1.EndpointA.Chain.GetSimApp().GetKey(host.StoreKey), path1.EndpointA.Chain.App.AppCodec()) - suite.Require().NoError(err) - - for _, path := range []*ibctesting.Path{path1, path2} { - ctx := path.EndpointA.Chain.GetContext() - clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - - // ensure everything has been pruned - for i, pruneHeight := range pruneHeightMap[path] { - consState, ok := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, pruneHeight) - suite.Require().False(ok, i) - suite.Require().Nil(consState, i) - - processedTime, ok := ibctm.GetProcessedTime(clientStore, pruneHeight) - suite.Require().False(ok, i) - suite.Require().Equal(uint64(0), processedTime, i) - - processedHeight, ok := ibctm.GetProcessedHeight(clientStore, pruneHeight) - suite.Require().False(ok, i) - suite.Require().Nil(processedHeight, i) - - expectedConsKey := ibctm.GetIterationKey(clientStore, pruneHeight) - suite.Require().Nil(expectedConsKey, i) - } - - // ensure metadata is set for unexpired consensus state - for _, height := range unexpiredHeightMap[path] { - consState, ok := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, height) - suite.Require().True(ok) - suite.Require().NotNil(consState) - - processedTime, ok := ibctm.GetProcessedTime(clientStore, height) - suite.Require().True(ok) - suite.Require().NotEqual(uint64(0), processedTime) - - processedHeight, ok := ibctm.GetProcessedHeight(clientStore, height) - suite.Require().True(ok) - suite.Require().Equal(types.GetSelfHeight(path.EndpointA.Chain.GetContext()), processedHeight) - - consKey := ibctm.GetIterationKey(clientStore, height) - suite.Require().Equal(host.ConsensusStateKey(height), consKey) - } - } -} diff --git a/modules/core/keeper/migrations.go b/modules/core/keeper/migrations.go index 0885dbb7ad9..4a25a9901ea 100644 --- a/modules/core/keeper/migrations.go +++ b/modules/core/keeper/migrations.go @@ -16,21 +16,6 @@ func NewMigrator(keeper Keeper) Migrator { return Migrator{keeper: keeper} } -// Migrate1to2 migrates from version 1 to 2. -// This migration prunes: -// - migrates solo machine client state from protobuf definition v1 to v2 -// - prunes solo machine consensus states -// - prunes expired tendermint consensus states -// - adds ProcessedHeight and Iteration keys for unexpired tendermint consensus states -func (m Migrator) Migrate1to2(ctx sdk.Context) error { - clientMigrator := clientkeeper.NewMigrator(m.keeper.ClientKeeper) - if err := clientMigrator.Migrate1to2(ctx); err != nil { - return err - } - - return nil -} - // Migrate2to3 migrates from version 2 to 3. See 02-client keeper function Migrate2to3. func (m Migrator) Migrate2to3(ctx sdk.Context) error { clientMigrator := clientkeeper.NewMigrator(m.keeper.ClientKeeper) diff --git a/modules/core/module.go b/modules/core/module.go index 02cc5e2b3fc..dceef4270e0 100644 --- a/modules/core/module.go +++ b/modules/core/module.go @@ -140,11 +140,7 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { types.RegisterQueryService(cfg.QueryServer(), am.keeper) m := clientkeeper.NewMigrator(am.keeper.ClientKeeper) - err := cfg.RegisterMigration(host.ModuleName, 1, m.Migrate1to2) - if err != nil { - panic(err) - } - err = cfg.RegisterMigration(host.ModuleName, 2, m.Migrate2to3) + err := cfg.RegisterMigration(host.ModuleName, 2, m.Migrate2to3) if err != nil { panic(err) } diff --git a/proto/ibc/lightclients/solomachine/v1/solomachine.proto b/proto/ibc/lightclients/solomachine/v1/solomachine.proto deleted file mode 100644 index 46d102bbb6c..00000000000 --- a/proto/ibc/lightclients/solomachine/v1/solomachine.proto +++ /dev/null @@ -1,189 +0,0 @@ -syntax = "proto3"; - -package ibc.lightclients.solomachine.v1; - -option go_package = "github.com/cosmos/ibc-go/v6/modules/core/02-client/legacy/v100"; - -import "ibc/core/connection/v1/connection.proto"; -import "ibc/core/channel/v1/channel.proto"; -import "gogoproto/gogo.proto"; -import "google/protobuf/any.proto"; - -// ClientState defines a solo machine client that tracks the current consensus -// state and if the client is frozen. -message ClientState { - option (gogoproto.goproto_getters) = false; - // latest sequence of the client state - uint64 sequence = 1; - // frozen sequence of the solo machine - uint64 frozen_sequence = 2 [(gogoproto.moretags) = "yaml:\"frozen_sequence\""]; - ConsensusState consensus_state = 3 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; - // when set to true, will allow governance to update a solo machine client. - // The client will be unfrozen if it is frozen. - bool allow_update_after_proposal = 4 [(gogoproto.moretags) = "yaml:\"allow_update_after_proposal\""]; -} - -// ConsensusState defines a solo machine consensus state. The sequence of a -// consensus state is contained in the "height" key used in storing the -// consensus state. -message ConsensusState { - option (gogoproto.goproto_getters) = false; - // public key of the solo machine - google.protobuf.Any public_key = 1 [(gogoproto.moretags) = "yaml:\"public_key\""]; - // diversifier allows the same public key to be re-used across different solo - // machine clients (potentially on different chains) without being considered - // misbehaviour. - string diversifier = 2; - uint64 timestamp = 3; -} - -// Header defines a solo machine consensus header -message Header { - option (gogoproto.goproto_getters) = false; - // sequence to update solo machine public key at - uint64 sequence = 1; - uint64 timestamp = 2; - bytes signature = 3; - google.protobuf.Any new_public_key = 4 [(gogoproto.moretags) = "yaml:\"new_public_key\""]; - string new_diversifier = 5 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; -} - -// Misbehaviour defines misbehaviour for a solo machine which consists -// of a sequence and two signatures over different messages at that sequence. -message Misbehaviour { - option (gogoproto.goproto_getters) = false; - string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; - uint64 sequence = 2; - SignatureAndData signature_one = 3 [(gogoproto.moretags) = "yaml:\"signature_one\""]; - SignatureAndData signature_two = 4 [(gogoproto.moretags) = "yaml:\"signature_two\""]; -} - -// SignatureAndData contains a signature and the data signed over to create that -// signature. -message SignatureAndData { - option (gogoproto.goproto_getters) = false; - bytes signature = 1; - DataType data_type = 2 [(gogoproto.moretags) = "yaml:\"data_type\""]; - bytes data = 3; - uint64 timestamp = 4; -} - -// TimestampedSignatureData contains the signature data and the timestamp of the -// signature. -message TimestampedSignatureData { - option (gogoproto.goproto_getters) = false; - bytes signature_data = 1 [(gogoproto.moretags) = "yaml:\"signature_data\""]; - uint64 timestamp = 2; -} - -// SignBytes defines the signed bytes used for signature verification. -message SignBytes { - option (gogoproto.goproto_getters) = false; - - uint64 sequence = 1; - uint64 timestamp = 2; - string diversifier = 3; - // type of the data used - DataType data_type = 4 [(gogoproto.moretags) = "yaml:\"data_type\""]; - // marshaled data - bytes data = 5; -} - -// DataType defines the type of solo machine proof being created. This is done -// to preserve uniqueness of different data sign byte encodings. -enum DataType { - option (gogoproto.goproto_enum_prefix) = false; - - // Default State - DATA_TYPE_UNINITIALIZED_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "UNSPECIFIED"]; - // Data type for client state verification - DATA_TYPE_CLIENT_STATE = 1 [(gogoproto.enumvalue_customname) = "CLIENT"]; - // Data type for consensus state verification - DATA_TYPE_CONSENSUS_STATE = 2 [(gogoproto.enumvalue_customname) = "CONSENSUS"]; - // Data type for connection state verification - DATA_TYPE_CONNECTION_STATE = 3 [(gogoproto.enumvalue_customname) = "CONNECTION"]; - // Data type for channel state verification - DATA_TYPE_CHANNEL_STATE = 4 [(gogoproto.enumvalue_customname) = "CHANNEL"]; - // Data type for packet commitment verification - DATA_TYPE_PACKET_COMMITMENT = 5 [(gogoproto.enumvalue_customname) = "PACKETCOMMITMENT"]; - // Data type for packet acknowledgement verification - DATA_TYPE_PACKET_ACKNOWLEDGEMENT = 6 [(gogoproto.enumvalue_customname) = "PACKETACKNOWLEDGEMENT"]; - // Data type for packet receipt absence verification - DATA_TYPE_PACKET_RECEIPT_ABSENCE = 7 [(gogoproto.enumvalue_customname) = "PACKETRECEIPTABSENCE"]; - // Data type for next sequence recv verification - DATA_TYPE_NEXT_SEQUENCE_RECV = 8 [(gogoproto.enumvalue_customname) = "NEXTSEQUENCERECV"]; - // Data type for header verification - DATA_TYPE_HEADER = 9 [(gogoproto.enumvalue_customname) = "HEADER"]; -} - -// HeaderData returns the SignBytes data for update verification. -message HeaderData { - option (gogoproto.goproto_getters) = false; - - // header public key - google.protobuf.Any new_pub_key = 1 [(gogoproto.moretags) = "yaml:\"new_pub_key\""]; - // header diversifier - string new_diversifier = 2 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; -} - -// ClientStateData returns the SignBytes data for client state verification. -message ClientStateData { - option (gogoproto.goproto_getters) = false; - - bytes path = 1; - google.protobuf.Any client_state = 2 [(gogoproto.moretags) = "yaml:\"client_state\""]; -} - -// ConsensusStateData returns the SignBytes data for consensus state -// verification. -message ConsensusStateData { - option (gogoproto.goproto_getters) = false; - - bytes path = 1; - google.protobuf.Any consensus_state = 2 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; -} - -// ConnectionStateData returns the SignBytes data for connection state -// verification. -message ConnectionStateData { - option (gogoproto.goproto_getters) = false; - - bytes path = 1; - ibc.core.connection.v1.ConnectionEnd connection = 2; -} - -// ChannelStateData returns the SignBytes data for channel state -// verification. -message ChannelStateData { - option (gogoproto.goproto_getters) = false; - - bytes path = 1; - ibc.core.channel.v1.Channel channel = 2; -} - -// PacketCommitmentData returns the SignBytes data for packet commitment -// verification. -message PacketCommitmentData { - bytes path = 1; - bytes commitment = 2; -} - -// PacketAcknowledgementData returns the SignBytes data for acknowledgement -// verification. -message PacketAcknowledgementData { - bytes path = 1; - bytes acknowledgement = 2; -} - -// PacketReceiptAbsenceData returns the SignBytes data for -// packet receipt absence verification. -message PacketReceiptAbsenceData { - bytes path = 1; -} - -// NextSequenceRecvData returns the SignBytes data for verification of the next -// sequence to be received. -message NextSequenceRecvData { - bytes path = 1; - uint64 next_seq_recv = 2 [(gogoproto.moretags) = "yaml:\"next_seq_recv\""]; -} From 6bb1543876b15be3714982a7538b9fe4c38cb57d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Wed, 7 Dec 2022 13:55:55 +0100 Subject: [PATCH 14/44] chore: remove unnecessary file (#2898) --- docs/OLD_README.md | 114 --------------------------------------------- 1 file changed, 114 deletions(-) delete mode 100644 docs/OLD_README.md diff --git a/docs/OLD_README.md b/docs/OLD_README.md deleted file mode 100644 index 66d96f330f2..00000000000 --- a/docs/OLD_README.md +++ /dev/null @@ -1,114 +0,0 @@ - - - -# `ibc` - -## Abstract - -This specification defines the implementation of the IBC protocol on the Cosmos SDK, the -changes made to the specification and where to find each specific ICS spec within -the module. - -For the general specification please refer to the [Interchain Standards](https://github.com/cosmos/ics). - -## Contents - -1. **Applications** - - 1.1. [Transfer](./../applications/transfer/spec/README.md) -2. **[Core](./../core/spec/README.md)** -3. **Light Clients** - - 3.1 [Solo Machine Client](./../light-clients/06-solomachine/spec/README.md) - - 3.2 [Tendermint Client](./../light-clients/07-tendermint/spec/README.md) - -## Implementation Details - -As stated above, the IBC implementation on the Cosmos SDK introduces some changes -to the general specification, in order to avoid code duplication and to take -advantage of the SDK architectural components such as the transaction routing -through `Handlers`. - -### Interchain Standards reference - -The following list is a mapping from each Interchain Standard to their implementation -in the SDK's `x/ibc` module: - -* [ICS 002 - Client Semantics](https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics): Implemented in [`x/ibc/core/02-client`](https://github.com/cosmos/tree/master/ibc/core/02-client) -* [ICS 003 - Connection Semantics](https://github.com/cosmos/ics/blob/master/spec/ics-003-connection-semantics): Implemented in [`x/ibc/core/03-connection`](https://github.com/cosmos/tree/master/ibc/core/03-connection) -* [ICS 004 - Channel and Packet Semantics](https://github.com/cosmos/ics/blob/master/spec/ics-004-channel-and-packet-semantics): Implemented in [`x/ibc/core/04-channel`](https://github.com/cosmos/tree/master/ibc/core/04-channel) -* [ICS 005 - Port Allocation](https://github.com/cosmos/ics/blob/master/spec/ics-005-port-allocation): Implemented in [`x/ibc/core/05-port`](https://github.com/cosmos/tree/master/ibc/core/05-port) -* [ICS 006 - Solo Machine Client](https://github.com/cosmos/ics/blob/master/spec/ics-006-solo-machine-client): Implemented in [`x/ibc/light-clients/06-solomachine`](https://github.com/cosmos/tree/master/ibc/solomachine) -* [ICS 007 - Tendermint Client](https://github.com/cosmos/ics/blob/master/spec/ics-007-tendermint-client): Implemented in [`x/ibc/light-clients/07-tendermint`](https://github.com/cosmos/tree/master/ibc/light-clients/07-tendermint) -* [ICS 018- Relayer Algorithms](https://github.com/cosmos/ics/tree/master/spec/ics-018-relayer-algorithms): Implemented in it's own [relayer repository](https://github.com/cosmos/relayer) -* [ICS 020 - Fungible Token Transfer](https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer): Implemented in [`x/ibc/applications/transfer`](https://github.com/cosmos/tree/master/ibc/applications/transfer) -* [ICS 023 - Vector Commitments](https://github.com/cosmos/ics/tree/master/spec/ics-023-vector-commitments): Implemented in [`x/ibc/core/23-commitment`](https://github.com/cosmos/tree/master/ibc/core/23-commitment) -* [ICS 024 - Host Requirements](https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements): Implemented in [`x/ibc/core/24-host`](https://github.com/cosmos/tree/master/ibc/core/24-host) -* [ICS 025 - Handler Interface](https://github.com/cosmos/ics/tree/master/spec/ics-025-handler-interface): `Handler` interfaces are implemented at the top level in `x/ibc/handler.go`, -which call each ICS submodule's handlers (i.e `x/ibc/*/{XX-ICS}/handler.go`). -* [ICS 026 - Routing Module](https://github.com/cosmos/ics/blob/master/spec/ics-026-routing-module): Replaced by [ADR 15 - IBC Packet Receiver](../../../docs/architecture/adr-015-ibc-packet-receiver.md). - -### Architecture Decision Records (ADR) - -The following ADR provide the design and architecture decision of IBC-related components. - -* [ADR 001 - Coin Source Tracing](../../../docs/architecture/adr-001-coin-source-tracing.md): standard to hash the ICS20's fungible token -denomination trace path in order to support special characters and limit the maximum denomination length. -* [ADR 17 - Historical Header Module](../../../docs/architecture/adr-017-historical-header-module.md): Introduces the ability to introspect past -consensus states in order to verify their membership in the counterparty clients. -* [ADR 19 - Protobuf State Encoding](../../../docs/architecture/adr-019-protobuf-state-encoding.md): Migration from Amino to Protobuf for state encoding. -* [ADR 020 - Protocol Buffer Transaction Encoding](./../../docs/architecture/adr-020-protobuf-transaction-encoding.md): Client side migration to Protobuf. -* [ADR 021 - Protocol Buffer Query Encoding](../../../docs/architecture/adr-020-protobuf-query-encoding.md): Queries migration to Protobuf. -* [ADR 026 - IBC Client Recovery Mechanisms](../../../docs/architecture/adr-026-ibc-client-recovery-mechanisms.md): Allows IBC Clients to be recovered after freezing or expiry. -* [ADR 027 - IBC WASM Client](../../../docs/architecture/adr-027-ibc-wasm.md) - -### SDK Modules - -* [`x/capability`](https://github.com/cosmos/tree/master/x/capability): The capability module provides object-capability keys support through scoped keepers in order to authenticate usage of ports or channels. Check [ADR 3 - Dynamic Capability Store](../../../docs/architecture/adr-003-dynamic-capability-store.md) for more details. - -## IBC module architecture - -> **NOTE for auditors**: If you're not familiar with the overall module structure from -the SDK modules, please check this [document](../../../docs/building-modules/structure.md) as -prerequisite reading. - -For ease of auditing, every Interchain Standard has been developed in its own -package. The development team separated the IBC TAO (Transport, Authentication, Ordering) ICS specifications from the IBC application level -specification. The following tree describes the architecture of the directories that -the `ibc` (TAO) and `ibc-transfer` ([ICS20](https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer)) modules: - -```shell -x/ibc -├── applications/ -│ └──transfer/ -├── core/ -│   ├── 02-client/ -│   ├── 03-connection/ -│   ├── 04-channel/ -│   ├── 05-port/ -│   ├── 23-commitment/ -│   ├── 24-host/ -│  ├── client -│  │   └── cli -│ │       └── cli.go -│  ├── keeper -│  │ ├── keeper.go -│   │ └── querier.go -│ ├── types -│ │ ├── errors.go -│ │ └── keys.go -│ ├── handler.go -│ └── module.go -├── light-clients/ -│   ├── 06-solomachine/ -│   ├── 07-tendermint/ -│   └── 09-localhost/ -└── testing/ -``` - From ab2e995b1349d5bd756c718738db5bcf89dbe71f Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Thu, 8 Dec 2022 09:37:32 +0100 Subject: [PATCH 15/44] Update release-tracker.md --- .github/ISSUE_TEMPLATE/release-tracker.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/release-tracker.md b/.github/ISSUE_TEMPLATE/release-tracker.md index d0ecc255709..85c2aaa32b9 100644 --- a/.github/ISSUE_TEMPLATE/release-tracker.md +++ b/.github/ISSUE_TEMPLATE/release-tracker.md @@ -26,10 +26,11 @@ v without deliberation -- [ ] Fungible token transfers over a non-incentivised channel works with a counterparty chain running each previous major version. -- [ ] Fungible token transfers over an incentivised channel works with a counterparty chain running each previous major version that supports ICS-29 Fee Middleware. -- [ ] Interchain Accounts over a non-incentivised channel works with a counterparty chain running each previous major version that supports ICS-27 Interchain Accounts over non-incentivised channels. -- [ ] Interchain Accounts over an incentivised channel works with a counterparty chain running each previous major version that supports ICS-27 Interchain Accounts over incentivised channels. +- [ ] [Compatibility tests](https://github.com/cosmos/ibc-go/actions/workflows/e2e-compatibility.yaml) pass for the release branch. +- [ ] [Upgrade tests](https://github.com/cosmos/ibc-go/actions/workflows/e2e-upgrade.yaml) pass. +- [ ] [Interchain Accounts inter-tx tests](https://github.com/cosmos/interchain-accounts-demo/actions/workflows/e2e-compatibility.yaml) pass. + +### Other testing ## Migration @@ -57,6 +58,7 @@ versions of ibc-go to guarantee that no regression is introduced --> - [ ] Add new release branch to [`docs/versions`](https://github.com/cosmos/ibc-go/blob/main/docs/versions) file. - [ ] Add `label` and `key` to `versions` array in [`config.js`](https://github.com/cosmos/ibc-go/blob/main/docs/.vuepress/config.js#L62). - [ ] After changes to docs site are deployed, check [ibc.cosmos.network](https://ibc.cosmos.network) is updated. +- [ ] Open issue in [SDK tutorials repo](https://github.com/cosmos/sdk-tutorials) to update tutorials to the released version of ibc-go. ____ From f5e02339d4b76e34e2b024b180e5282cc78a1e18 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Thu, 8 Dec 2022 10:55:58 +0100 Subject: [PATCH 16/44] docs: v6 ICA migration docs improvements --- docs/assets/auth-module-decision-tree.png | Bin 0 -> 80522 bytes docs/migrations/v5-to-v6.md | 100 ++++++++++++++++++++-- 2 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 docs/assets/auth-module-decision-tree.png diff --git a/docs/assets/auth-module-decision-tree.png b/docs/assets/auth-module-decision-tree.png new file mode 100644 index 0000000000000000000000000000000000000000..dc2c98e6618328d7b0d9e49f0f41fee310cdd873 GIT binary patch literal 80522 zcmZ^L1ymf(wl13Bt_kk$?hb>yy95aC?(Q&nAUMHg2=4A4T!RxlxbuepoO|zC@4nZw zW_3+Q3X*5 z2v7pzvk5HtH;I{yssaRrHx&d#U?>E{BRDJY5CX!D1p?y87y^PX0|ElqF}qbo0DK|T zLL2Z!K>>meoQ8vdg~Wh>2B#pw9|*`#5bv#lQxLL{pZ`0p3Q6e{Z_3i5nr4t7i?<_@M7OrCa*@3kNVJo&&$I}2A65>Go@dlx=WL9%~J@PX6s z+00}l{}gex5hT-AP$m&~aJC@fVq#%pArnF%At4cPHvht>Dk1gn>fm33WLB=Oj(p6_ z9v&V{9_&mG&X&xqyu7^3ENskdY>eO%j4odGt|p$0_AccAYUID#k+5(vbGCMLwRW&4 zd2iRm)WOYFkc{lTqyIksb)OcV*8kI!z01EJ3;cl0?^l>vnOK43H^!%I?P+1FBVlc4VebOIH6cz;4uOBV`CnK5=SKgdruKi<)@>BXkum|#QN_l|B>|{rQh$HPs!QZ0z9Pe1NUD1f93tVya4n2sQ$+&|7!~W z$pz1$5P|^nf3F821k*-i3J3@h2!MpBx+mm`0j##B9HzfD5*7svHulNc+2rQgnSBl6 zVyh{5rr(d4=#=Q_z<%MMwVo0p$|Btc#GU8;`#wTL3ongo5B#U=4^R|h2$$5q4;I#NO+>yMz|S(lqd`1Sz9Wqu!LamV$fhhl8?pnS`Q{ONy^C#1wrrImq#= zl}STt_^KWP0q+ry`20_RriRx zQ*FZ3mzzxl<-c(uGF4YcQ#7}`i=Xubsza}e3+-Ys1ixfEh@#Tu%f`693#j56R)*q zFGMfrgWhaflv4d4JalO_K?7r2+dCynV%Qb2zMVrF-)UhAFd;k8Qwn}UYBYi8_W~J` zYsKsG8lQklzNdLW59BqE)eP+Cv6(^8&YrWaOHZsk#&qe>CH41;Jy-kXPz&82RU%Xu%uKiNm(s^ zA&JHKLn8j@Dr0I&*7%J@i_Wd`5t0t300*+zJ5E8JStcQi)VnoS;)%^>l6k5S(YY5-D1zMpcadesj= zad~k=IY%Kl;af{3TeYKsP-;wtFI(36dk$5;>Dgz<}v7j55oh_WU z;qf~m{21Si#*Anz6zOc2@c*dN=qjgO%rO9zL{x73;+Bz&OOz&5OfftJ0A`SQRA-lJ zR>S;05MKZ>UT}(_Qc>9bkTHSw6BIO`Oo1FgJQ%w9%sp+f>$>}+VXWSIw!2#-_vmS# zooigN{4-d}h=igkOW>1cs8fY?PoXEOQXB}#hI1Rd;fMFv6SeqmNRTl@%V)3=ihn@r zU4zAB$#Xz8pU8&ymhe=Sq}Mas)CVBmbSr$+HH_^d|y(-q z4#c+k4MSAWuYFyopYogs5KNWKhgjKXisT`$Pi7%uuMV)?WOhE&0>U*xmFedlZ@+`q z&CxjQde{!9bSxt?@P3Bcga-e78i#aK63+{zlSwQGaK*JqnB#7km)Q_ z-HU#G`ID#q0W7si@W9IqIb4j828JvFA{kHm*7N?5RVpDjHb%=nPqqcf!o^1R5wcGi zBx@2&dFP@raup8(snjM1UK9-{c1-W@-FgOCH zZ*V153fO8vl4%uiJfxv(I*aozzAE%+iXRp2se!5{aBo2O;;jrJQ6&uG*h4exY3Yd6*v_q|A9k9zG;Q5U8gDT4LbW85qY97K( zCRpCmfg0iKRe+J&b++}Y&+&=a0_bVxPuCxs%n_8r@LS0o{T9bRqZ;+6oOoO~Kkglt zAh4KqwOyquM&oie%5<@zoIbycs?{l_8=((q)4*wKgrNC`a}pl#=4xbQbcl~Tks=v|x@;ph=*(}Cs3|wwx2Ms19toG5S+d#|U!_vUWIU$}t;|l%WOLAhMuct! zV<6gi@0;7jJsw_T%up%tT?OHTk}W_Y#C6jkB|%H17_ssB%|5xl+ zE2naoJ!4TV2*=AT#+>SD{QKp_eI#bdWS;w!2~EbrC-JvGOt)Ij6u>VED}|eRhUSE; z2!DRP-~+TFN`$bWKm?GIDWGS_va=U_n0oV_?W6D(uP91>;-6u@joi#wP8rP-P0izRI-@_{+63QLW$hq8B!6eK5z6Uu?}>Hl8~8+> zNl+%3z6Duv_L2(Lo1W>3+djE^!dX%F@q3(`TsH%F-1fL;we%7?5Rl?Gg z`zo%K)_>h547vTDMrY8jL(o$)>RVVgGq$sf>yu6}IqcrrZTBOp`neE_4;!;!dQtLN zy`llvj{}3HGMGl4|A0}VPzFZThDvMhRGVZBmi9+-hNr2I$ z)B8oLdHHyz0kh07BHw#{wE6BgI@P#-=p`jWfl*C$z&Onx%N^xA?03yhG9>`YZT?H8 z)bE!FRICp+q~VI%!6{q-Rwqv*bq1sO0^bbDvYkL-$Y5Xe`^OvlYWCZS4jR=1CE^tZmA}^UehrdP&ljVKCZ~(S7rP?f*Z7+G1F6UwJHQB|>Vi6rX z$gJQ(G@h-u`cZ#S_!jZF7t_O~A*CGzivpCCdDv z0|=o`DpJ>{{T{C~D@9NXHxFq&o%i?%sF0)!Ke#1vDI9rHi@u(a+Q45u56ET6u1XOY zbxG#?wuGt$i(4|~J4E1EEi^;QkI-pmy;-_dJRt-%$AdSW=2sYvH}cp(cjqpjb0N){ zm9_SURTG;`BtQNpslG=yV=JrsMf`4!r~Na`Ia&ia1FLOj zb&}lDdj~CgHqB>hLhNil7nEO?%-wpbV=KjqQRy`s$|+c8uE$HCG-zq+QSyHba9=7( zr-Idf|M#W+YFXrc{~69I>io4&I-|*vcG9Tx!y;;~a+9o%>+u>St=du~&RpmlvK1qa z!zP~@uMSkra^2^~k%@t{Bx&aF(;zQD=a9ujq#Suz9cSx?xZTg;M>MbC>0^5pkmJNa zh7y=@*^YYpRid=y^PAGNJN6^ z>o&F{+1Sz?EpTUY=YYn5LYS|?CeBueJ*I#yVAy-EMT~@r9NYnuu;Bb1*pN|NAn5kw ztdg`}bAhq8V=y)a;`z0CXp;UAlVrM%(G|$;@D!~+(=>gFe;H09Wn9|~AmOZn{BZjx z7-4S|#(`S}XmO}rUg3oKKH66;;1@SI&|U0JY)0^19Y{Uq>WLGWqTt|?IwM$~j*yvM z;3}q>cxV79oau6O3gc*_>+?|neZ0L*QBOhHJT5{5JMxh*gHp|J&&b{l4opR^C}fNv zjST;ULcW+Ru*izdno_vo1MBe(!#;P!#-sp2OJ)N}Q!*K5=f#CeQ9=$ob>b)tN+cJI z1HMQE88K5#>{Kyvk(jG0eh*f+eSx*kH@L=Kme&CrORUjcM8P&!u>DPom2Ta|w8;Z{^s{}?JN8w@iD zrA`H%7Mj9=_2?iU!`s%a*W|>$&uoO+mZEuac^lMJBRq(7Dh*i(-Ir)R!g>8^UL#E8 zL`uW~rg&*xa-Kg-!2U0Ay zy7%E+@~9C2#u*0E;5KlQH~>JFtB8%{B`1w-^!1LUjGsH<67ZNLMjLCxX+1>Jir+)h z({aav8OD?43bpdz@Z8<4LwizuA0p2>%O0_zW`e-4-l2%bJpL)1jOuT{q_*3$p$ky* z-|anmbNOT_tux`C-$bV~AUm6-C2hoyP|nV%%b@X0wmid^4?KRiP%BM#QCihR7q^#1 z&RjiTb*s#2i)pK+eFV)rR$S7w0V2qXb?3fYT1SZ0G>0M!2ix0XAW9_?%m+(k<|r;5 zQxs-3ygPHf=ZTHDe(nzaB899El8F1z4wSqeH#p7~@-e4R3EdO(u$gQ{xXi=_2IbrZ z&0#aJZaSoBJ8(4fHnHN>pPmU}ik^6@cb0!1WHfp5q7d-ManM$VmUD_o850fo;d$y7}WR@#AlOk9VSqY!C#Q;CisD5~i?n&(+WL#$z84P2d>Zxx2CRK>=(1j} zWc?ERImjHVD|ssx_SfQB6e6_S4%Kg zZU|twxjh{5s@I=T@>Gr|WxjlY{1sbFYrp3$Dytu1%f4&I7qEqKRDUA2`zX4$-CD`O z2=ZE0JmL4mV_fkld>l_~H`Z7=Z-LAe_CWyZc3IQko(Pj1?LB64I3%b3Y4bRj5CmIY z4+P&gVda9j9o7er7afA5VgX*i7VNAxPl=JJRLzf#dHvI#QQGIWa zyMbk9)2$Zlq!Ta)2{85y>%NqKA{n{%ilzo^<}2e>VO`9I&ir`%FOkZ%%0lIDtsVbd zQf99Up=P;V+c{}Q4xo(!Pl05fyI;~j-E0aS7s-3#b z;oz*Jh-)%_%WSakJA<0n`3}d*=#0&rdcEBSMCR;rzIctO=kW;gC{qYNT)_s3h-ZD`%b0Y+;G?UItjK zQCFm`WPcGJ_lds9mi9EFUxB0gU5SQ!SE3J1N~?XvMHF)c;C+%fnu2}fZt*!V9KL6p zSlWcFCgkL+&-7-n&x`g0c7gg*iAPOe{k{7wJbM+talk8PQ(H$b+?dN|kU#=_0Dpha zVCJ7OYIP9tDm&0u3LxUrMOqgb5(s%h>IV-?r6q4NBz5>-eE_3Ip(&%G0%jYg9MwjM z7P44j%mDks2vn)+Y~KFc)V~x#-+2Sjn z9_>;Y;593CV{{PHOtA-+^xIEql8)u{Tb;3O8qfQ}1a07JfHLCw{fcL+!5vSi9~R9l zEu}+c%k^|E%`XpbHYLT|dg77XZcl|nF`sH?JhL4y=V+tVyfDG|7)~>+Rvz!~gYF$p zmT6pOysMHpYVyMCUFG$_xt69Ghhdp)0YA+YxHv0LjWY4DZenQ>^gZGLm~v=R^l6L@ zIkLDJGiuY(HA)$KlMhPF#J=~tQ6_$E7=W9Q4g+1$#1%HuW5jcP44ZFrjXXXVWL1RT zQSh&W6RU^pj(F}T!r@X9wgOvO$>Mwd?@$TK{5@B>IPj7 zU0JgRblvk@wQfo)QNF8~E0_Ra-`|EV)AnM#yiHCoH7Miwrq&;QqVp3j(m$1ivNIu9 z0R2d1NUMg`W8|x|KqA)U8L_c>UIY-!2lTZVp_eBzP76WZKC~4(hE(m*wb}834{XrH z(-wYA4zokoF}P?tS#+r@vRkX>pCoEUIkx4pQtcoZjKdF-mhhk8yAkbKTTV=EbjjxN zNLnD?G`kNpED+SRX*}VCap%&M@b4@Y-4RkajyTojl8XDPq2r4}rLc^H*#t(idU0(k z_T$1>rzQm}xrF3wnygvB?3-eZq{3TPZw?I`o~ncma(VQaR<3*D>7`GR!E^DW5b{b1 z>FM>+$N3Xv^gfN7=-Jg5|Ki7n5);Q;}BZe zpw!Ge3;E;29k~70`le-p1=SS>)>}4%m9XPwVRp?&Ro52?+gMC5ZOwnm!CEeQ$jAhR z@Hg5=k7AFKg3(SBS=OY}sXA<%+ONOdu}FIBsNIT+i+Safzs=qvu!D$aGK%;WZBLwY zV|C8fI;6j9J7j$O22V@t$M=-WHG;7fgUbvuAJ8T1={=O0;qt6Jfr>vir#Ga6_bI0* zPhz3=H)#ee6f*dAh`qIYwX=_v{U^FS^3Yp=en+;rAgbmOhm|U(aF8nB# znIH2r)^OzW@yS2qBpZoe6unFAQRMsDrXFQlpi5;<4|M$r+Y#$%w2ziQNq)}%lR8%7 z!x4FM$w&M>0k9T~OHVd8R!6+jX`S#O#7^?-cNVt;C|JF3jN3Mh6<-N^G99y{)eR%# z>sYB7So078@3^*}2<50f{X=x47W7(Xuixy3lUb4N1-*k7ir8#tYjc!;vZ?EqU3ui2 zseZAU4u!`IUZl9Wx#_I9^jHP%_E0Q1OtZLt-3*tZ^SSWsvWZoM8ri+0q+4bR`1vhE zqlh%Q-hSRNeeeF;ZAMeJ?MfZt&OU>*Y)p|U^37yRI^NE7ys391qB{+V#_|fEIFX!u zrfmWNe~{C!eT3NtIhzZZ40vxE{1dZi zVrDG`Oo456vkiIPC7Z~|&bsO!p(RWHi;66H0j0Ff=ET3!xGUI6b&C&lrI_!plOJ{f zAm}xnlhH2=YPUFQI%ek158YAfIA%FNZ#9fqh~4Vl0yYpU)zUe$d)&ggCKFF109(Il(RFC6`eez%ziOXl@ze-HQeE8|Alrepi8z{?c zmi@n93!RR7Ib#VIw3=~tznap!3*#5G5)Pp&moM_XLYVp7i7VR5f1*~*!7(%#tXT!C*V_Z#ue94+w_@-&B zB$`d3xh>&G15QT$~D_8DkN^H1`|CrWk{x@ikY*eT<%DO zbh4^;GP1LASv0he?8F(yZH=gg^(nVsB0gzsSlAkp?kf97di2KxA~PCZ@ASK70_=Qm zb7Ymf3Dp|%j&`fnXxmd~1_8q}Q!BTyh-BM@u@T=egxwN1xl25AJNO6?J;?4&r80vZ z9~N;J({pt5bNZf{J#Xv6Pm8y?yUhrZweOj)otD_tQsc>M)X{-GNmTLQd7^L#Ta$@@ zZPzx|VWA8d3?D1J3h@5jjLZ+F@DkglfZtX^20rzGm@ERwekF8Qkp-Plj$PI6vTn}A zC)8XK8=1A6^DqbEff)ken;sk!0}^RMs=HFh**=~*I;|VYuV&!*d&D2PS6NWdz=TT-TSeB|p20P89GeSBc>xIHVf5GSRUn`ef z+{$uT3<}f}^AVlWK*?FE4tyEO24D%eoCTB2%kN^akh#4reFKxlaD-qzI)A62S_%sC zw5Xqgw>f}#?&~zt@~IjbFMe%1IlMJfi}3M6BRpTyQzOi-Kp=HjY4!#A%)0VVXGaT7 zbZP~lMCnb&b^Q*4j?Bi6)#nqgv->i3^GY*H0k~rF3I$8D79UjN0=TNKONuEm6ltoN z;;3BilbZGB%;yfF9!#=+LA{0FEDzcfKu&hBCKf(k&p9}_f-q9WQ0qLypsl0R6UcQh zXPrPEq~GouR^aPf#5vAUDns%0%ctf|4s&7rgM?k|;n20$u<#}dSzK=JuTun%qTvFU zt(6JKEuv1exk=}`$Gd69siot6$5X2wXHq+YClYbQnsiXSxP9DMd(N{A+Bwn!o&B+gwU**&kQn@UO+?nTS5V` z_+`Z2Wc7^`;)TV<1Ss}5>gO8&A3Q00U%ADTd|MLP!X`Wt$DC?YJf3&0*M4Yc))?`_ z4;`LRYzXMEq3-2ZL9 zBF~|wdO9C)NejRm0a9Qj7bhpHIjZCkn2EQ#Ju&IteTt5+`egQZ4c}_Hp4403Fp?ds zB^0v;KRk6wN33n3qt>R6BQ~FNF%Ge$_Zx+%B9lbpZCPa(Id%MYkE-#ltqzO-tZR0Q z`)3<&+d<6oh#6y56R7R}{5JiyjhJ*~ry!w>YbJx1cm``#rq{ zswQ&HIk9LR{%5-J_ONIMs~16e3bO(;q)YeY2Cv0%B1#q&GIsevOHA6%V_en+mGpW0 z?dYG&R2!?!AC%>qG+`^86*HgQP}+U>B$oVLaB0|HcS8cq+9yY%zBm3Xyc<%qmjQ$e zg^gtWs^4pK`d$x}Z8~-?=PKpb@AZz1KS<-XeITKxDQTKq@_OYuny-vTy0xBZTcmJ3 zSw>x~-b4!}p$rvXe?qROT8e3hY_OhD?=KK=yr;R?#W=kZpbW;lGM~bzC!@zSR7v`0-nk@|)9-SrbqN%+ejw)VAK44Z;PcSzg0R?OlEj63}D9zGn_<6t- z`ZLDn0%i)QJEv%NF5gX15llW6M}Afmo884t0%Mqw#|%~bl-LJ-(n=;W%(RFe1Ow)A=-m;fzTX`h(d1`Oi%uQ2-%w6-d7e&y1ofz&yXXv5_mq(5audTc40BJpari zZAF+g@}a1zDQ&dDL-vty+}O}vG^-Qgv$J}Wy8=TNKQ{I#&c~f;hYPq_Isq&g-;_Zi zCWx$Et#{bw{UF4A;{+qMae6p<49Q+T(4)`lDGb ziTvLUf;r=!vnaey(hAaKnuy6`E(q8I61V48AKSf-gP|khjRzW`wPjW0d0OF;BO4Pg zGsMk${vv2r86@~fAQ;iw7qJ3@voC=b=MOd>67AAl(@q%z&#^VQ8f>U7P z=$YFY&tC1Ydi9RT_@c{EX4R(r*Z~(ia3%knZ5;%xUZ1V%L6-I?6%8(<|aql7%& zh~=#DCJ9SR)f4VQJYntkF48E?vW@s(7U}=chjrfVK(ZW`*@J4?eoiB%_V?4?o)0riU{OO6 zAi2O_0Vps6l~Sy_u5@0ixbo6aBbe>j(MmgtCp|7=lE8OO*T%h9Gy$ z_}rF2gkrfoy%P6cWojoevpI*^+@cX@tdc7szPl$}qI3O$u>n0`d_rzVC{{h=Fjp}r z3IVSoWC?w;39$Iyj^zOaGcjALJ|pYfu$i6kG(_ikd&u$J6?JX814r+o*6w{AuBmWT zKn&<2AWy#fdyCDLp$Y~}!EE-FfJ)Sla(X;STG(Mkpgxb|MI`GE$nhLLW$KGkvNt!| z5e*rY86b_VhT-%2rftKY4>S+3lB1Wni@JQSPp>!NF*&&INwg$aU?IN4k8IH}oEG23S>-4(-m|em@!V(1) zgaua_w)kzt_l9;maafp+KPbr^T^dS&UPQjiGwY7kvnbsX27!7|7rzJU506Nc7{@LZ zXy#HV8IB3~Ju>Lzd8>8BnkYdQ9M4svXp!~Xzl29$7Dr=~l|3Lb8FWDYEQ(^?>OvQ* zOLH4_u$MC(e4^Bzw1}dA<>Z>jZ?r+hoG>{LBUA0WAHAG9qgb>Olt6iZL$l{$Ql}{5!h97L%P_Pf zPrn;5pKY%3q>EM?76yBYIPu|6yds!Ry|Qe$O38EybE^Opzs*sN5@`bYN&wT4*D~JMyUv#kUI$vFYd3)4 z3;rLg^#--oG%-_4vy#JTmB*3`wbMbv_kC!?64Swl3NDaJLKD4{l9?&1>8wA}`U>D@ z8OWsYHD4vfdw27fVyuy3_FtUXE_36{Ou60^)k1+qAxq6EX1x}iXYPw#xM z5M3+nCN2hRJveHeSLPrk61n?Ubdd=^*4Lhf23y^A-8gFY(u zSSqq1<}bw}CrEasl`GG`mzO~+|oO^jjUk4<*3cRz+JDXzyolS5(at*T2Te9z$jzF@-2r^kz zoH$H6K@(5$s;*Bu^?+A~mUbI}X2G8@K1Kfg!G#_<3^U>CG0p1yPM6)7UDM!%@)5!E zqZw10C&Ie3!+w1r(HqJ}M}U%^Vx!soV^wcPWQaa@r5fRlRlpDYQ|vv8r_$3VK25lO z7_eJ}8I;AmR0iB|R3^2kyS-p&udD@$Q3>WjO&;=5(@dqA()IZQ(cWkp&a3?1F5Os| zjuS(3kH1>iqd&G_)F>b)E`KRWbeLqiD?0AvbIpTaK`Eu7kolJE(7yQt-OHF1XLg-J z_1)uyo_4J#Z~TlqhLKT#I=vAT<2Md0J>~n(@z1z9ymwl~_YvU5b$=(nvm5D&Vg4sf zoDO7(w}H!@vsRQzHQSR2`sH}rYoR*Cb#HPRTNPsA#^$ieQskzksFLrWI=hZ?iEksT zIPGOr%=LnhRQn@!3?vK&5^uMM|0Z8hklj+=IeR&Op>z-^4r|IXD$`r#J_S(-DMbg3 zm}NFtQMUhCioNx}JzD#;C!3A|_7mK}O0~0jdSCVEl3>hDB1!e3fDn=j6Y#JU7Q6_$Q-X{8*~fpWKu25-cZ%5mjRb=G>Dd3`I)X2=Kr`@KPR(J&FL zcX$V(M_uDZ2ul8gv)Dx9BoTQ83dPV?metP(%@wxaA@lWro{_vN|EzVorQmxG-ok|?dHReC6?7cc~KRzT( zQ9fL{xj!pIN!}~>kL*s+x;JvA++}HzIk5wY*WztshI*1Tq++w8eQNZTJG2cs@*`=n zM?|50oVF>hr0Vcf!7!d398JZdRfwj>a~s%HqXmQ*^fBtUV%!ZscpyY)-5(ZXwl><6 z33O>EgOEy$lElnEGeA23HbJSPjFYZ^d6Tj6Xt&Oxq`#e^`=zC-)9LvthNg(%oIz3; zklp18xHtKJ$fAoz<1vxI=#9NFFNAFAzFC(Pm`b zfy-C8QO{t#=;je}_f~PO&}||R?3(?7!(ov!59Y>Vpwh?Z&a~7p$GHCVX%u`F~oQsdLs<3VN0J&?bxW*r_rP_UK-~&A@Y$F1S z=7j##bc83u47t4lb{c_Z_=%I>iHhtg*X;O+E#E?r5P_NShSp$EV2XScqal*4c?x|v zKpkkv*6)|8lwFD%Cc+@``_7dP^oT9u#w0{SLMfXjzhtp2+|g>b9r@}h`NXJGcTGZD z`%C!E^s%x9i{eqT7Do1Mk&MIl){f71PMbi`55U-GG9*@m?%{=<_ z9&(7rS8Fl;JI&FcKiDNwaz+Y?24+D)C480Epq#E;i!S01%A4tC{*D;k9w_4*74}y9 z5*d$%>}PapAR;Rho840T_kw_I+~d>}f#>_>v84dVHaLm(4spc#Gr{ca#@rtlCOH`# zE<&$aJjm|qi+z^y^$vfNO2Z@Zm=uXhm8o^o_wwmEGub7NvTla3?Ud|u$ zwEAEwE>w`-Iye&&grHkbMD<4(%9mkWP(o;0ZMge__@p8%}0-e(gB^fhb(*ON5V+tj)e5 zU+XiHd#-v}`_tkSFlVh!Kpu`H(pafeI_WTDN~R`!Cr$a!5CZ;jliX(}?M^OF(acDp ziYJWHH1EG09UvS5enSiL9r&$+$#U5R@7cR66-8}VHHTfdWy5_Qxs=)NK8&$WfWSTv zW`D+wIw7?pIe7h^@6kQoV;JPu2Y72BJEA%T*SA+&&k6^5n?OX< zm!JcV8YP)I|Hf|hutsgGFH>Q0P^=U4qZ&>pFKupZ4VwE|@EYlFMX5w}24=2*2WVt% zp#v%^7Wj-uo^>QTbzlLP?7Nou8;J4}s~QjQp*evYaI&m3iC@7wGdA4tNGi3S8Ci(0 zmkT*R{&)Qjug^+`?ZJLWDdrbfFrOPwaaa-UY%3*&0BLp4O`R}_kM+z_ih9w0mQ$vq z7oAq?1^7bkF5+M(M&(8^$+cEL+y?|KFk0MJo4PHR)9Q(@aJ+yOYMwj`m-LbwvEp>5G`Xb`7JDmyfFaxPjHYB~)vDW!%2P=Ooe@K`c5=B+fT2?4Zek z7gq04fw?k6mC(~8L31n&^pXCrcRn4k`!RrEEeO@>%mi*mNaR_3<9h{j?O+sr$3o6(ha%bSI~? z${D5Hy{t{&4RkN5ej?$F0Q1#HwmlzrX)!3|16}{jMhWIHnA9DC9DAN+?6PDNlf%MV z;Pl6B%51THp67VS4cfDUR-ks#4L5B&PP1eq+p$^T99>qlNil7DxBJM!_wKH}jECI8)ZDW!zDf$M}O14rO#UCQn96?>;D53Re4C6MPYYS!akvqVoGs!k4a zh^I|7tsUuB-pFMumxI;Nitmb9rk0O_55xIY(=FdeuMN4`%Sd>3r=4+0RUcHcvS!7L zN2UsMofUPkt)7mqJSgDw@TaU35hi0ULmdf#upLSM=j1dDtJ&<$X?H49r!r=Oire}? z#$u(#z=qUmq(QEVE)72L*whSDXutpN;T#hXnXL_loPF;$3^zo;ROG(rmlSSp-Y3O> z(x{kb3Eb_cD63M!P%x!B71_Xf@I+cmYiAzO&9Uk7N+_1^S$u%H(OW?G73|l(=cAsa zPNuNvqGKlm+wHN`aaz2&?M}2Q?A5f?E2`B}V;f6+{!3IamIbmr0x;wL zTN(v0GZQ{WrIU%;e?Q||Vp#8v2!Tq43ZSFt?}D%B%TzPsa8~oYBd{j4nY`&6mpoqc zvXF+@Nkmpt8PT-|s#Hqba#8d(qOvpeQfM5y)7Liux$oepTQ7Z}$2sjAW>Nqd@9!D| zciXe?!`?mad_{h*zQy0OeBuo6`UJ`Qa3wK8|6uTdWzxp46(S>YOK38^Zyb$0od;M3ZzPi;Og0s8 zcNxdPfUPe`d;2wMh{ff(hO@F6iwP8 zPdvrs5}}&%A`sbcgO48*zX&dk<^iQG$meArc}a$vRHtyTsozs()}=)7yIfjL|ItL7 z(-ShU^5HB7V-{z@OIFvJ5KHG)@gQcvyKh}UZq+AzQj*0DyjU!kQBe^8`WS}&U~KL&B%HK%JK(mO>c~V-B3*iPr|f8S0rq!x`mQ`D^@d= zAVbEKl00x_3JZ-K94(W$5Vr7L}QgiGYgTCJ`weu z5%@(_e;4lMiRw3C1|CHe3bd%Dw2O}$DzC_95X-eo76{=UM0(BK^kTemFnCPId@ z8VUW5bh6fgntZ?*;fEmSZ2Ah@`uO2+xeOUrts0Nka(8BW9!{%_(ls8d&VJ_$0!at- zM|XXR8vnSN4mJLz)zzrS`ke$vXu3i%lAQNQf*fIE&h}yQ?*86@${cg@v?o*|(~W|? zX0fx?*b&a0)d(dk{kBhG=U-)5NudRQS&h(KGD;c2+jT8jMTv(ixMqaeg8fvFmhx$I zn-e2TdmnfPB;A~^^Gm*J!oh%12Gth~aBwzBUTU>A2EXer9<2U`Vo*MxMi_MLZrAefIt-@a!=>V6|l6<_r*Laf3e`FzeO~Jnx#pE0#oocm zjKqB$70uJC)=6l{a_^oQAVe-tEom4-sikBV&d%uPk&MWXZ6qezr7>CDc)=6&!Z{Ok z>bW3Nc=u|^$fCwPwYmBy=qL{@5)J(Jk;5d97QM%tS78l#EbIO42LIi~zy1q=&mEzR z2X}lUzD~oXQ0_^SMz59P|M2vU;gL4cwqI-;6Wg5Foup&W#P-CtZDS_(#I|ianb@{* zJLf$2{O_l_-d%gw=2|r_D<^J1MMpJOLMB_G)dH$IZDsp^8omy8n9sLkYc`oj!0(m) zXvC_m5{i+~QG%s`>=gy=U46LZ6y6_5LJ}U<@y}|d5dtB#q7TEt|L5;U11HNh(I;6> z5T4dD$(>y39Yv$i>FJ{n=4`nIX6}GF-*5B+ceY572gMxhkK0N10}RPLRIo7O7vu(7 z_=nuvH(21!&WvuW7gRpA{16|P>$;1R$f3li*h0jn4h`S!(wJzJ#q^o zd$Fu=&CLR<#kk$xNTLjYe@6OG*LoV=7dHSbNEb?p14K#S(e%l7`{@)wlbkFd)P#1s z1CpSg_ei%Saq<|}&}Zonk;;sfK&^!Yy{iFKm{oo~+S#E1ahV#ITltL68`xcFSHPUa zeH@+1*6)&(YBAOP{{f!sfzENC*{>)9Bo;A}IC_ZpK@YFDp!+|K`q!iPKpPu)*P9cg zoT}K_0r*_^ItftJ3-2MK#NSKCeSW_Na(XMHob}8Oc<{+1V4=P+c}^qffbp4M9GDw! z3j(0P0W|k(;^o_G>E(+EYN@czd=vB_Bp*_c+zAQ+ir8ERO)X#zv6y|3s8(sheIcC~ zDKmcJzuCEe82$9f+B0)m%IYv*w!q!mjvm4A%y+5y>;aYBSp`=Xi3!Rr)k#rfzx;44 zaV7q0wxk~@)P3OA>2gOf8k8$Mq7bA}ZvGF1hi5fdG+eiL8u&dTrqrwmwj8g;FRw{^ zvwBak+-w3f7~>#}r@Z!XJd3?hss{G63|6_j5MF~MuU11A44Q|=LxZU`jD|K}&B2yV=nJU-YkfU1h5&<1H0Y<4X!kiQ9!11Q zD2LC5DQMdAgE5^h;|rO6BnA@7ok1DsYVr#8qxildTI(?Z?R3lmS$7a45$NH(C0@`R zMCNI=;f(t1Xeu%2^kC=cI#j^frbyK$z}sjkeQ{(78Tzmju}?H|iLXTcg*Z?8x{F3h z)v*4Dr2xrWY1CcM zHJSSa8!(l?h5aPv3!EDK)#DLmQB#BW4v#;|3X^~B{*xv z^QY5la#WHRO!SANL$&r8i4ix(B}ZnVS;+2x*_jW)m#0I@Ed?!!2kDkwhz!0JtDtMc z!rIv1@fyqOdu=HDrx_kf+Eo0)Dt)HH(Q|J+UQU%kt+A)VoG~bXxfJ3H+TDtpv!s&z zMZ%CK;@9K}St_TnzV07SfWNhT-T*Y0_GB^$L&tLLSrYmWkFDUOza_{=){BW7RAwU+gA?UD9N5!jbx^%SHEK4hN>Kpg{0I@qUMT%-Tx}X#+|0j%vh)ugARiP6*Opa5(>H+T_C}iD23eH z3|221o39=C-j0+%=A&ooUI-~TIFL9NbA5&xiDeTcD0tul_CMmmVu}CiU7`?0J=U4^ z(fIlplkUi-F=djCnfv;b`geT;Ymj(u`UIbU%EtqBePlcWUcAwX0-YLbYu355CLsa4VaO zISU!ruZoE&f}zh(PUi>g8}iqxFal~nNKwJTMEocyUtT;7Ca$HLUo4qr5$8mPw&;12 z^^oU+zWdLb8dFMH^NA=M-O#D&3wm+CRcZH_k&WZXr2Mx>o)YZ~e&4z(`o9zzY9Ocb zLFZ7K@kM6huf+qd_IGV^vahl&(IAtBS=*Z^-}bo=Xq3*WPDzkwz9+x39Qvq`0KeF1 zB11u=1kOvV+-R=*J;M1usTD{7!RD&xfC^w|0}|XO{dF6eL@Da*gm=Y zfWs!dKors=cYEFxfgbZR*EGaMyhra5@>4BrcfpmTRl7X^s^&vGfuNffyAtg=<`g1l z-{creEkO`+6wqp)S!NXy_{iY0E?@vt|IJO}P!-udGw`>wcQ6DLr2fdqCW8W7B(a;k z^@bhzG%9H(pz=>@{LeC_&HF^kz|24hCyPH}x;2J&cpR|uYmHDYI#S8o0;0|eq*p4C zdDp&|3Tr>`Ktx1@U{X=CF1-KyA9M1^A}(hZl7IHFQ9l3EE9Vu&Kh(3LnBsHazPKg) zH+G5ieIxuw!X7e|RB3pfY4Ewr4Y}tFhf^eWFu66}v)gS^^XC}T#S>J(qUe5hoN@yR zXwbh~&(9CO?jDEVn6rNxEeFu zdgJBgsU0LdZupVcM;7wgd6^^{ZUwvaYUDJ+7Qx1H%KThgW)u!&OR@F*SAVfc|C-asw!^>|y@t!L7|Bf+r;~oFg`Tc4g_H#Dz(Baj1B7U(Q zb+&Lgg5mGYG|62!4Y*o;YpVyQ)(r==Ej_YKU%TByYXCeRU#?0aZGbipvCLoAWc3px z!&~=P?8nuHKC>Tvg6;uP6b8T1HENBVE&l5ddK5Es*ehtk>E7-v5%j&GF8ihbAB2j^ zQF=Pm!L9;R{>bNf2rsDHMf?@MXPf<%K$n8_dh67myzp0dG=pZNY$T24899m#8@z-N zbn8NGcve=7d8`i=az;mmixD0<RhMwkZZDKma}RtQ=<^d%ARe{p{#P#pN!T%G|fP zfYpkP%`%*U_wrY{hr`oFJ-(p=S*jv}!RYL|H00we-2OzpDqRtZ^($_INg*Z(srn$c zzul*DbMvMcpi`thB}p35C6=_U>UpOAbv1L!d8Z#5+o@m>uZU8}hAgt+Wo?j7(SX~3 zy>?o2BTJ50?t^!1eIwCzJvo(wK-OJV;Ig2QhRPcf0!tD;?S3fVOe_G85n+;E>*IYi zEs&n3pN`3ca@lt$;6vCcFV8~8CM!ByA{hL1^ZJIT3&_$tx}@hZzmX zsr}8iuiNVgLQzP17|SJjIO}x5G(6_0B9asaWgkvW3o}F;~A3Q$+mW*T}@2uXd zzi7wq^$u6%PY2n^HRDV9RAh+zV4l*o2nB_=qxQ$%IVWLzBl6m16tx++3k%y-=xgfX zULV$xjwMQjSrwUPDkNb~CE(Zh8Y(G!hK?+OK5xysH)-WUlYgyG!m~Nl8bh^rTVllYEMsacSx!BNU;8hBp#2|A|;J0&f95iSOP1AK@`uN$*8ikaA-ZNN^rcjb! zDCl?z8l6m@uZSH@3AX96ZhUx3IZnHO>D#~z2tkEOtHa8#L5sC={S32jTQ%wn+IsB zbK1zqEE`~QJ&85t%)oxrp(5We0uIvTifD_MWs#KmpIiw<+41B7Rgafq*-`fDqj%(D z#1LehzglhNXY*?s?p;&L!1Ox1r^XO`DEwdMR`-9RplqX0Wmno8p8nKf+qotMwHW3r zvr!2B7SdROLdIduRiISl`l~>9eI8zV{pR;ynI>^(XH=pq*Y?ev(G`*#T(N>YOQG{% z5>*4xqh36QV<3)Gja8i@x8XZdjU_$CE6g#`Wr*AB2aoBwZS?D;q(L$bwO8lM&iNr9 z&>H!!#10NmIj*2k>7V6k_}|?o%&HrUOkRI<(t+}Mx!;&~#6$bk#@!r_u& zu-m->8&4yTZMTQlaRHL2DYSuaFeu;!CtqUT{3=*(W|iQ_3JXgo5c6l&e9U9YwsH^! zCxU4(tnMECQ3Js0O&6}4Voqo$hEX^qwo!D)>Rzb3IH4kM==^sJSuja)2uY-?V4x3+ zSm6V`qKp%6Kcf6F@!k)&POk=Un#m_NHH&^J87*q69AA59UZNI{ZV!L3^kMU)Ioxjx zvkGtrXw_#Pph+c9&dXz4&P9p(%tRBP&Znpi!TWQE_-T8D8U6e5PYpy=_1}oZ)$M1* z0`*5cI*N2DQ5E@#%K{HrWP^^vQOJf5AQmT%v^a($m`0!?~lULksFwUI!8!f7P#HZ6-5{W=N<)p_>S!c!pQNzWcsJtUpQ74 z6VqY6HtsW>z}?@RU_Yvq4cVd-FjW=E64W)Q9v9H@%U1j!&)S5g%^QnPfaL*-%b|z{O*;~k`|EB}0zA#vp z^WKXACWhwD{NIrp;^>%i)32eD|MLM9sLgkKBo68+06GM|b$55<&}=5jiL>3f4PL#R z#|#qs#Y;+t?~CEh{R4opghJAIlZJL3rCDCSA9{blzxnb`3Bn#`MUyKFgv(+I`&lz`QZgBryvhVo62Y$-CP6_p zPO5F%!|+#D2TNVJ@ThV7lLy_nN1mjqImH}uYIh-DJvT3VIEko7>DqXCeRwG=2J2d} zyGuBc5`%D1J4Zd&vE}(mcHn@}29ACX)9nGx?tKeiRcH7m;LrOz1epP8-RvT=o@V}@ zcYMYh@ZYE>DaP~*MXv*_uu0)+3iME9RGt+;Xt^Vk$@4%7fTuL9+6hv2vQh4bZdKPl zNxvemc>f9=0pMpEn+9*Qmsbe6f<&(Jfnc?C!jE+`BVu{Ic7d83V?9tdOg{Ojtr-$+ z_JJ|9c?-2PHcJ(n`EuPAjXA5VQ7GZuR(f@3KX`98biDy{^$^38Oi?EEE_Gi@u;-iH zaafJNn6^mA1BH>ty^FxXbke?#eUsJb*-q~2Oy-T^$8g=(#1wpG2m>uN|1Ku)EJBLP zh4ysq>$Of8deZQfj)1Y`=(&O$d4tHrdOdb};CZxyhQ&lU@9wVWy(x7;$@P6PbXh>6qI`70V%7I9DEkf(1PT`+tK1>J4`Uqx;mBW^SvK}UC@vzdd? zko?mC3ITHR>OjB8Jv%>vX?)zd9jjqXK!V;dLF{d(_fF-|O5Ifvr(8V6Yp8gd$YZc{ zDq$6UI2%Rx34bR^_phrjC-@D#mG^}uzh!OW7HwUqH75jXAdLe-RWbord>1K+2FW6T z*;EKC(S=jeAt8eBWL?}+=EnBrg7tewx^PcMSB~^YE6bun#0m^=&mmBEQ5*a; z0;z_y?ubDuio4BW;OUTSuiNmnpHnAmQqU4%%-!|Hw>KctDTongd@wqY)qQ`PLTz&O z!mT#++p4^wxI9FAIOYTbCThWBGKGr3L$6o?fh9bdrmw9>`qj0_f4(XSvlb;L^Wwq6 zgaBcwB>St}zf7UgeyV@*RVZQsFB)4<7rYl;0x`-i6S>9AaP)B$?p@hTE5A}kS5Yf{ zre^A=!9@}X;f#AL?|Rqdl`#~XD^^6spO0?6BX~667j7T?vMypT(`Kuwlijfk6n-F8 zg%!-D7m3yu>>c|h8qapumAn_U4t$7{ae6z@Z8=_(WsHP{CU+KzM});`&c4dq^{o>p z1PH0i+rOEH)#dE1M3k;804Wd-Hk+2Zk9yFD#ZKT1cQv3QCbs zL%&>3C{fCZWj`9w$|?l%CB<#u1!ZcpLTqr4(t-dcJZ%+oIF&`tDuxcPRcZP__bl>W(sBztA&dF1nNZzOmT2%*+FQf# z0;xb0vq=)aMGnUb9eTr`f*+fofahw#PUW=9|JX?Kt|$T+`bLW@=^=N%lIPu%M3r}B z955X+I(ikE_WR~&oJ-OSB%lR*Gf5pBrENB|+oiXElqkd}U$eJ|_+C0LlFWVolZ4H1 zN>P|PD5v>^tSy=d!IYBGKf4gMyLKBq5b#gX)>rc%WOW)pCqP5A1c{j)gg;ety#faj&z(S!4OQ2ewP1sa9YKnXhaPs!LTt9OrP zB@(kg$8%!mWp~|uV0rJfJFsQCg}Zl(2qlpq_Zrb1`;yPii@IPPEDVG^OEl}7`1!{G zNO3>=&Ko>ZDz3cSbpL&X;Kf6N=FdKH8Wk&++Fm2G_~e|c;mpSMuzn&2N@gWls*aL+ zI_1#hug`8WC6W0lu0RU;8$WreMRL`adam2VCV~v1O|hb&Zk<#qJ16TXr$u+D+>k~I z8oKaa6z^4`k&zN940W|~nwUOKE{=kGSsCRCs$9Qu_mPqzmSLsVe^1v}q@3lXP^R+3 zT0*^2o#mBDZ>(CE}y*k7*PSj2f&{M;+~62Q0Wf`Gqrkzm0@ZQ$r0-OV%(nSP(#J z%qPT0%2&(%%0W&IPYA#2zlY$ya>8Px96`XDqQAN$!x6}6e(5gX;TRQKZVpL6ao^o$ zBt3KTTOwNNrY<)gmY)qAR`PM&2-*|&a}yK|@3RZfESygfs!)mO_l~zkMJp9I0Xaoo z$fOg_2SI{`Iz51JCBdr3K>!@wPmlVCqbKC%JM*p*)`C7i+X*itR)_>P$F*ef0qxAV z4XP>&wEqlCT|NV}k#0XR8VHCo`OKaeF!U-r1G2D^dUW(5OcfjEysaXjaM$Mg*cs_d zw0gqc$B>D~KUxKQ`Z=FBN4CLBLVwd1&m4!CZEhzUYQeC02z#SZQhB(}1!K03Z6Wk3 z7DH{4b{g^ln>FmF!>OUbe936wG9ik4-%A7c_1M+bI37RtFTpb{+?r{z*IIcyma9gC zZqg*%wJ_dyR$LOvz5#xV^?7q`J183=q3}mC(*a^?9Q}<0gXYh@5L6U}z=Nt-Hgp7q zo5#~Gc2*}x&pTQce5~8W0cReznsp-xX%ovp0ILcCa>_ZBrrXNqm7zL`rayRdlS_qC zxjTay1@p{F%(9o-B`IbfzS5bD;iD!?2ZN_xvy|mn2v#4GLE$;8zFL;ly^QuIl* zh2U(Z^fGL?aowOGw96Rj5fdnZk;N!v+mGzGS0Zt(&~(Mt{%009u%tw>;-SSk3h~!8*=@OT%B!JBUcCmIaV~jO zK)+(8{GG++8d&oF(~DY@FvaHTaB4!QdaJE?Gl%q-vrwi#3`J3Pl~XqB-RU#=*H5dQ zLGg!>(fHdPXLI046K=+3MU{;40>t0OP3=DoC7!R@dp}sMV~z2}T5x1O z_fHE$q9y(o5J#8Ar;n>;a+u_QAhD2GmnM61u)Vo+-Q0fJ>Vd`JVj?pe3BcT;;Uz>W zXiIHa+U4iNg$>4qB~RRRwGLKXW`)^vC4f*}%q_XSo`b=CS5YY!5X8aTEf!B7&t$ds zB=3jMMt{u807!@bQg$K|l-@aBPZxsO#P-PFrgPqGb;atyPU&rUZMz0c8m(g_agDGe zL$Rv@^3-W{k-L3&R!9iYJ__{E={?X}8r|6^!)aTG+FW|jCu8wT*QKI(ER%SI@MIH#U&{z^UijN?+Q;my?h^<&|47-P!isObsihSUkU(mD!Y8o$S`B#Y{F zots_gp5#9^xNZqZ=?jJ4i3%3l##W@aPk zen$N|F5xhzQaC(Z#hK$6a(dRC)QD#K5JL&ojVP zwNo_6F#~oA&*NfB!VLHQKFr8@;t|os%tTuseV)xOhu}V_#glyU%p!MJ2K|r5WOq+i zb>@pj*ZkJBfL@gyJX!4m;*hOM)b`y>-tC?cB;a&03VP)B9V{8IH`T%E1p;Y%lPz)D zVQxm37pbJsu>Gvp&-u}0G$mL=Cswo6ySgbmNvk%Zi9a}Tr_V9pfHa=Tv~AAh4g$Nf*jGV!Qc+}XMxy5=NO!Hf@=U{6^XvWrER~t=hFQ494G=?MC8xvup z-4{&jp{hSZV8U*@5mm;3DjP0TgH6u6KtUUR=`wRr$n3@7wIy^Ioh$e%n$_;$Lidj0 zHa}yo{K#?%N*~rYhx9Jepi^Ds?iG*)=X+i8*M*=E^pO*Ac9o$ zW-lzB42o>*9}yFy4t(sCkR&%x%#Ai?h;n4&kz1ayF`)B1y}$$8WL=>DbQoBuV#uJ9 zqyC2wVoCGd?u~(yUXret2sk2w~f6XWO4; z$C@I~E(#4$6Kc2w?6sK1E{DGLH*7xmY?g?v>KYV}a1=k~3@~3S%I3p~H_4t`h{1Nb z0NFe1mjXq=TCXyK~6#M2yx zQCvrwLia*TiIO?HB(mLFzB!mfTT0N2(^Ub zB~^YhFY;ChCML#*HIVw0>~F53@r;KC)5TSE&1|T*57!)u3`Mb*eN*2cWb40Us|<5g zalN!DhZyPRHe5#i{(#I~6$+AQC(%ZRvPcxzKCXiHYH+2DV~*2mrMaqCT3V4O)hO+@ z{q>85(=#dkh4)p2<6WtcG=qiSL)e+;#%2crGcuv@=t5b1wUtIy z1h+j%c$8nlqYFL%2d>F8vS!Gw|5h4fXkhleXmO1?Z(0@_4W}v_jVv86m1te!-YQe^ zaP9+yu_#3apQ=mx7)aR+DgWgEuQ$jqg_(w&%r`n3a&n5!L%~QsQF|>sn;}j_TrOqVA}Ln~zDT zJz7EKCuqpK12Nt>eW{`1WYRyuE$Qz6UA8B zAz0w=#42xR_TrSGS3hig|JIEvXJ~)rb-tu`z4PHcMK>JH+tVXV(=FUxL6w_Rha8rR z0?|6G+vWT%(WQRqRmmR{jxF+iksi0R7E}HocSz98POi)|Wf&G?=Sm#~nzQJYK8Zmv zJdndEgf-E%#a3?}`;xL1M*8|Je74ZOT7#V*mEA32%fjfpo3OK{Gs~74B6ra2REYx{ zGz_$=m&fyig;gt<6a9C|H!0no;q21wrviu%c*tF?%r{0qVBvq&TeMlIHTqsZ! zg~&*LkKE_}{2-Ol&a^n2fZdD2Hx_xjPaDQ?YSkAox6fb#7nojc(5Jr??u<*GCAw;T z90e15Jk|#%662q`GjC_OPo3_D6HaH-R2d2Tknq%R9Hvqfb@midj8(%YV?w-JJGM=S zWRu}biu3hx76;P%-<)m? z5=$tN16gKERpcU<8*#SZ_*>j&go{Q6MIejjq5{Eujk;H8uDC`sz(xGQ8k%oUE1|fZ zU11Uc6Mh6JD0!BHjWn4Z$jBaNgIolTyLuYGh{q0f8)EUvFC{7CsdZ@4#s*mc%2@sZ z;JU>~HIej_849q6xK+|vUdv@ThNfet(+NPt!c78}|5E$g|4E?@492^Lltto@7jb(c z(|i5~#rLQQtXt{_a3lG*F}+1adp1I-=Zjd3c2}dy?KrHIs23D;Cg}kkX1)VUFe$si zsKdG%TI8FOSn;gl-vP0hOZwxsDb5>8d(ehW*&D6FyM|6%zfq+UItxU(F>n|os>y7; z88jZ&ZFb zxUhZ8YHyCfjYr=-s(5dv2^sliRvtWc>y&InDupH>Z58+zhdGOYORK)`Bq(R#KR6Qc zl6GR8GXAAjCsxl``~56cFI|bLBsi|eivdy(JT!TBtN9W)y2_T>&ksJsgE5@?Zr%zW z+$(D6-uuL@DK>c*7zqCmFizsf*B-UB`i2&?7EL!ltyt!r>pFU1HVN^dnpP;f#xHV_ z@`5m2(g<8UH~BRl zs5Ex!%2)!JupbQlX)n7pN~oKxITw5ZnS?zNU}mvw>NA%dAHKLb@C7VEKhkiTsYZE- z0kKgsk0?8RY3Yi5pKg0ndh=@i%Z|z89QKDnTSI zpSLI%7t8(yNCadyWj{b_SV$NVZ3(CjT4zlmdfPn|-V%T&oGE*3wpR`SgD6OBGO7A? z+}fh<-O8C2xu40*77K_bV+6z|6N&JPdu4^f6QccML5%PE?d5S_TJ!9o5k}6blaI*| zcGTjy+n7;S4+y8W+8YM)d4As*%UB^z`RFZAEe96i*{1W@uIZ9h)gYk< z9tXg26i({ycGb7gJ-1=@CBd{h6g-E)aX#QMr9>Dp@yLLvg61_{LXH*}Ke`yiQqUNn zTwP-1Fg?s{Dvai#h2}nvJeisFvea=06u1&V6UMMDD_(Mx<37d8Kz_Gc4*=mscY^N; zn{4<23^Qr#&PTYtl3p}_fu$G@`Nh$y7H$E7o-Rf6e+dtvKG8qkxOx*4p}#~ zga|DZW^{c+(UB~~K4E4%S3E7|!&HCO(U|+^zrhtbHnJH<@2(+6>n);zVu%TLlAy;4 zK@r=CX6NmMef4H1N*5x&Xrc+rT*K#D^=3D`AC+ri1=qXVi~l@<@n;bAwkwz#bzWB3 zb2zoAp_J4+L2;2_+vpSOU4+!9EA0HfZ@4)9NK0z=L-(p2dMY-{evo-*M%>b?QOVsk z3;Nmg_=gMs=)q6bGJcw5HctnGO~QHF`8E}=WIHq2>nxr#s-d%Z>rJlT`@q$KzTL z3fEqhaianr~5s)FV2ta8kiTpq{V*YAyIk$P<|NJ|4x;(|P5e^?AeK|_N7 z&6uGBcwZX41k&xt|t>yXz8Wk`t|`uUbwlhtuz% z;B4R1AeZE=%Vmsa1g(9xGrD2MpoATP$6iF*Owmfw+!}ZKZv%PPjTVhc@)vb$yf|Mf z&gnLm90i`zz8k{j_#tG?z*_?MN_aBg%&hA~zANLa_Cv|@INxE&#&g?ZR&NU z?zah^>g*=fZRu1=#u9Dk;+h{03?yYhz_IcC{0w=@>yRNUhfLFkVMdn*J)Z-kdzR>m zv$nudmBkt33VqN3^XTgNTQBRM5c>M&dG)hub_d~WIpMcUmp4a5;QU1-8BPnAAdFLv zDyj3y`~gHbv1Ah9Y8S{HgOTk#ewR+nYY$+J>u;!YtIMUxk6k7Q(MJF6Y3qoM>GAg? z;wOi<0dA4`MfMaO4lI zkw+B%chuVh=H37kAyS{S$E(}bO%PC?wBC9Jx5dGj>$zU%u{Gl{E)Xn+x&@lag+c-` zy?I(NRH(+^ zlD-66k9Ra+ArfZEHLZlxijlALl4ReTBZre+jo z%@Il~R>(Fuz9D9PZfglcrwy?4pRqgqAea3%17hmKVzut9bW^3~8KaiUfUVHv5J?$N zpZ0y?Hpbr1Y3Q=<@fVYvsWia=^}&Yf3+9B)pDl%e3#(d=q`PnT-DLk~2KnC;EJ)jX2ZuZh-lei~RW&ZrlEYjV_tdKNsSK z>9<74LRvg@F9<+r$YI3aL`+NVz&~`cA(er09f)atL5%TLdKAjS$wwo6dAua zyR3PhAy6PD(;$g#AnHs%9ysj~#n!l$(?hij>98&I&Ojnwt=Z=9!iIk8BdPIV5=u)h zTgtI$yJ+^>r!$13mZIks;H?J?xm2K1yD%)p0>e*!aUO}2R>W3MYfk-iAacCh+UOnd zA=%jo%|r7y&FG@&X=0(scr%nk(TA#2Spo#5VU(|;yHLoI&)D+wTNeo*ich=T9%tOp zha(ubggNrW*sV4Z2+i2&FD>@Cu3tbDh~d zj?j2)#w=#Qm5)-jvT9(|zf;MY0|e=1-C{<~`|TEa9hCJ`yO_}CFA-ruyrI#|tkqic zg~U8VPi65-l%inl&bP?yAfF=4S8DuI@0})&aK)Hj8r&d(Fz_OcWbVQoOQK_1o&Vx=WVkr7{&_-PzdqmEyeNk<|2NlOhDO#x$fI3ELkd| zCX>9Xue3j%^hP{g!;-^abVZY5BJ62#3rc=)HUK8lVZGd+gqYr2j^?@`e9>~A$_v!E zyxWH4;P=uIgUQ1ZA=+h_>y9(~ySsl(lnM7i@g9IK2%C&WDHXRmv#IY7;yg`wQ}o=2 zEqX5nSD*Jwdg^U16|{z-o6OE`sgkdgD6qdn5aft6;LFH8?roeh-Fyv%!}kVcQdvdT z4W02>@K@$2hgxKQqUl+c`>>ma@#LW~t@|Dvye$Zmm@E>r;=t3raFpv2V%!p!Q68olk=D zC1ZcP#T34?#aBHmY#S0F;d69@ek3Pn^~b{Z?Hb1AV%=irNegcHs>m^6AL~P>%_Uc> z$%d(|Eb6e(nx^v&ylY42v?!QhwXYC&t|{l-ysdK|rQ8V}2rYSsua4^@d9n{UK=Iep zb#mqO=rum$vBQ04D<0S&r&fBsg*R8PY9!_V{nMA56i&Pq_e!1|lD0o@${1~+ali5v2W+60wJHvSvRQepje^F8Hlc9Mf>uH%>*7Elg6*! zLO}dvV|%=6x9r(%F(xRT5RYfG#qlt}PP^3!4vYQ=N0vewmt+iKRreiL7nJo{2XeIQ z1t9!qSh5$>Pw zj}tDTd{!5inu7v@C7aHZ&lDc_nP)JZ^YWgg@DQWo=p~G6K9ZZcMyNbq>h0Myg2>st zp*}imTXha5;_K)GR$FDb*piCh1>Dfg=_k@-?Jgf`d9 z?{W!_*@E7@5!j3o%7xN+%=oh0UQgAePkWWpNi+uh7Iq&P&=skdqrtR?w}J6w!L&H@pH7p{{Ytz;VgVkiqDr2;GZQ`q zgn!K!Yj6yQ!Xk!lcD-KSpr~RA{qXfvIROcYdcM`99g%NJrzm`-47dwIRRQ7o(7TD+ z8rAz%|BiU{X%?-Q&0BlCT5*H!e1ct`xI^E_YK??^U(dOAjZ>!{u1S}M;`rSi6ggxb zWkBrz--G-@#yIV?EfCv`hGGUkN#Ia*7~)CTP_dy8j7SXhb)1GcJnqafnw*Z6cl+WD zWnWkd?44R+$@wYtiFR<}^MDnB7ahPe!IvZQ{+MqB1Y91ItSaeiLX=caa^g!y$Fqg( z)(l#WlYCIN z8ohYR+GO-$viy~gd~R8!~k0m1D@ABvE=X!k_j*}`wW>+8o&-?r6w zZ&i%Tji1}Ia&I9>1m(UT&qL@jbB6B>Q)R!FDi;-m-i?!)^)}kG`CKRWjPMO8b*R?3j z()e6$`nIbX0u^r4&+RG|Go&VyU7;O{Fin)}>H8rBJnmRBOfz|%WeG$H$2+NR*-%b% z>hwuVbseUyLRplLO>g!_P(@=vm&|X0!6-a-{FrD$Jn<$_T;qHmo}fbcGlNnF`@6}@ zq&JZ6`#VTN!x=nvXniN~$RCqPbAb@jaC{sT+)P4 zE+n#6l04Frq}D$#;RDG%)MU9`+0(PA29w786K70XLrZBg-7)7J?8uYd~i_JRsl^qY9b*I-e;x+{uy3NR0 zDx9S8Z&y7Uyb+#WH+1m`47G9ki~^-71N_JTewM2ntnOt|g`in=QxdLYP}H1I zzh-I|bo^0R$1zqUM+~Zvh{i7(XRHZQYqHV7Q8E^!n?*&TLoR%<{qx;ADlD;Cskf5M z9Nh@|L8lpbJMIlgS!#5zOeWL5=&RjCY*#5Lha3c~ez0aaS5INC+{o{YT^}u!rLGhg zLJ@b;BR{Y0rae2qSxKHe0Z$P-r&M@8*RJ8W^7bZbK*mhs}1wF>Ffn!a1FoSdZZjA3o_w||G`KEyTd z8b6sWjLbe!y3Q@P#U4oS{fm3OQg1P*y~7@X8vLu?IF~E$X9RP)-UHJFrFH_PXF`#{ zXAJdeZu2b&j9ny83P?c(+`hd(ZvNq2Vct$-sL?js5!YH&u%>0mQ#XQ>G#jmDjIf2P zLe)rhQ-jT=)UKmkPaA3SxVQZI`^@(D!!px$;c$xo{f!Q{+%J%l9cYBLsdacb%QV}l zbdAt}<^hKj1mqJ#n+u+jO@!X>RAdCX8Maj?__BkKG0V^~O|J)5)3sQcY?4 zC0$apX;x$>GX$7MpAziu^!v$@q`K``_lG&4pHWX~xLiy_!|e=-9~vYo7ASJQ)jL3s zrqUZ-6}ov6T%#s=D`zC`lFGlMmz+y8h2e}{r-h+nc-@RpSMFfSDQkq{K(q)$JD92# z<4|K|dL zt>rlp2_}w6T2XMY;eQdaR(3E)ROQ%F!rv!J&MzoQ@rVD83301U!^1l;l{4x*;S>5S{f2$Zr?_$YH;xFt8ogjv@wyy)ADOn3%SlIYpQ z14?OBWUs4pL~-z0&}A4JdDJ7^1^Ejl>Sab>`_s67 zcJV`NM`^4XzWbd&A>RK3OhL20pY@zy;>3x)%I;$v+p-7!ZQ0}4jt~)})Iy~ma$syT zdi3btN@{%SgM6f!{<`;{Wq2tr+~4gm@tNriUctt=pFYdNo;xrfZNm7>jR z6q(3Pv(M~*a0i8#<3f&?$9<|)sp2JAj_B1(w7lJsIziaW&0Du)%!DbpCn6ClvP72& zJg8p}WcfIgLBhLE1avVVGSZ|5qT#-T2|inKQd5uDCeK;^74U zV}Of3MRT&3^~&PwQ%!b4pwIUF96S5M&Uw#E^HPaJj(M2NUe5meu*ch4XYGM$_ImAU!S(FkH#;2fCYxZ#z&rM=6O;=R{WA6#FdWaGKgB(< zbDBFxR*W4!&_yKq1Mq3`yep{FA2jj7J(@l}yJ6Dcj$#Zx0&;y;4?q917LR3-Cy^w6 zo#1~Aa6Xbq^73!s_;?pMe@Rf7N1t_J=bhulyZzax`m`P9VZB z&#a4kmOk~JWm2DPf8PxsEof1^&6kT#Kn8?nR(al?Ca z5xb5U&;u3nXGXdD9r5*~X>wHIn!&F_Ed4)H#F=lz!vD2;U4p*>29BdH*!E|yOZh*R zbt)m%dE;Gs^y5e?I1Kvx^M8vWuY}@NA>%u*kQ5Xiy?X_lBYO1;D{o;Oe|-Fro;iCS zlcr6_>h)WZJV|`qxN{dn#WhyZB83o6(v_ILg(wJ+F7d~ti(ZjJ1@XcANfE98V00Sw zEq3kMhkiXe%lWX>rY+4EAPv9em5}oyq2v{qf%WqYA2mUJS{MTY2Aad8_G!+XIgO^R z`+U%M!nAkqUi15uV;9C$oThmPhi~cLm2;1GugCq}J^$;x2S%N*1BY*Ae3}mqN9h~( zLU}qTDFY|Lu7CHU&%mMBwCztMju#72qDIHm@gq>8Xc0psnODEIO(Ex>ml)6=6hA6b zrA&c|qlclz=cTamuRUnksw1{;|HG7HKE=P66*-YE3=o$TJ(6eyCeBWY0S#K{;V}>f zwi{SMG)v792^u74@Tn~x!_cD=et}2lpkMO(lt=4Poa?(!WqeCxU0NUWvQ8eop{-jg z_Jp*ceb&2I@HvFiE3CXFah$9=_;gO(t?yvSA$o+Dy_MWKvdIEJ1R2t$HIv$&_busu zpm&2G6NOBEXU{j9l*GSTI07A-(A@y>NFwReKAn3HiC31> z%RSwzcM?ci&+$(?Ue)85xI>2ynQXdG>0KH2gI0B@399>K`nzSC2KJLdj<`V0!~mix!F8U& zWYBu_Zm)-N?(Om2J+J0-FKd72v*uI$xu@&9;#}YDcZX2gvGEo?b5d~Nsbn(mhehbx zZv-NVd#qb`AD~Htuh6|qdn6QR%bY|yvEQQZ2U%APu@oHg5O<5F4Kb)k2Rs$dX#CWf z=sRc_E{d;0gI_tS=l(+vG9W`iL&OaUikdX@31nUPh*#oYFD;{stiIdt^c_j_ck1DS zb@&$SSSw7H9sDrrL8dGHt`2lhmvPYQt3Qzy||(dl691hrj>OLFQJ%a=7AW@q6l zSFPJDE|1QjTj#dOo;8d7ARU~0H%y$fTsV0|#J9i&jTddU@_Zim0!jF2X@=v#xo;2l zdCvVwy3|tZG)!Y)efrFIEu(EQPJdU*^)Ze?^YU(Qn`b5&)X=-$O{DD~%h7f#&loaj zeQcZfpiyx?GtkB=t>Dm3EdybpIFla$w0e^;VM5~@Q|mC9-2O3*ftG<72q%fA5=h#? zr6nQl3vtY|hQu@N&hog2)=!)$YR7#?Oa~g0bhf3SFw1i|2EW^CyI*AvyrFAln z7K=C^*dFtdh|^yvFU?0ha1QW@59{N_^RQ~WRrB#9gyT+)b@k`TGz|(r+wm^EI-|VH z<-~^?3~&N)(w#WzfuSSEVBV4+ktj|~#22$&Q^pQMVTVWt%2@Kpra!*?d2?g>*Q4-p zy3{y*{sP)}>xbn(u96?V=c1zd0tJvzS}AeDg^}EdZ}{P2$iv~{cFHWqGyx~>s9Xyu zy=Bl3q6Ur)>t^5rOwS@}&W^to9XsCj(s&Xo<#HKEE>T=W83I5~i3>2xFz`-sE4^(n zaKWc{5^_}}Hruyvf2D6K{w0B7y-Fxq56jYP$dDmJjA3`JlSGBYfrOGCPiV@HqD`8Z z6Wgf}cU7ispKwTmz;qH<263rn47P9Gy4Ads;4;t?3LRv3=+MEKp`&4Rao1(i2s4A? zi7@k-f#*bN7La?iBvKT2D?B<5EYJ4H(ek1=oOKY_Vz1Yj5afps4a<{&v%ka#gVsgQ zFoOpVHf0Di$Axu?3o?Um)u8{wP_$*pan_qcb?5QFg7HIH#d>j+>S-1?3;dM15wm4jONlj|xpT zW(659F2P*wavavJTW8+C`syoV?45b!Bf!Mb;JpUofNAu-Nqo`oC*Osbn>$S*axTb6 zgn6egPx>Wg9>T?-xZv0_p5_Sc@#f{_KT=gEgmMh%fO7(`UAMvdq*NuVMENRKo}%Tf z1q+v04`rcX5RN94_cZi?uVX^OIBMM2R?b4DtUN`^SgqQ36{pw7TpiIx#})C8J!t=F zA5;vBTf&L>N+_9^4xYKV(Gjq4V=k--_hJ@yIxXg+&Oke>%u7ehT!8t^ON@(~JDn)g zUMUxQI&&7Wqls_PqJ=9jJ%Wf|OY=@ICtRFahD3tiLDsHaYtm@DRScP1!U>sodMaTM z^L*xAI8^h_MV#%oYSqdRH^QUNm1!fD?OY;$-_IBw|Sv{Zdqk;rQ1D}f(D`v`xadYETT8_dP!lijQwj`M1 z7u4hvL-W?{+qYdFSLmsPF!O_f{T0O@OUxG<0#5ueknod$(GGB_20 zL3Ad~@+5#HwoDWEVCKVt@g(>RgopEkA3O~D8zoH4#}6fYym@)~&moj{Y=pvR9qjw} zA6RqdEi^{W3zeu~RrsQ|xP{v0>X9x;t_bHL&@u2_ewYjoA3n0?E?8(~DNx)hQLd_0 z^0R8z#?6k)F5*V#B?$uEIdNgu#hmHxLGv@7oFCm-agh`8py9a4X^8fy;dHbtIX&j# zqD=0P&m=|+%E5?O&k$0k@ls+*jzsp@3}-05EiKs(lkt40RlD3wkywyuD(uS1&>a=I zHxXf4Ns}gZl_Sw+JIj_WGmAf;NthUj2NBm=A__Elm_{PbzU!sGd)~=S%2`d5mqbrF zPdlzyufCJWk?62q5^m-vr_C`SL8l>e4J1?|-gUvjXZD}m9m{hJ7?dj}eu-P<(zPBM zbZ35Y$|PJQWW)#Kc#$LI#|QC7cVF(|)TvV@A3r9vE)qP##}Az%MT)rUVtq7zPdNF^ z4;Kc-nbxiNpdonX`~COdz0Ai*N0~=36k3A}w2`Y{yLKJleLoko7ypQe;wC3^ru0Sw zGksc5H#+PeW%5o4S`4U?PvppvP_z0M;;t(`y7U{4r14{;e%l@x*tsPtSE_($qGTYy z#dms)*nbA@Kl&7O9&_J{dy_(F%)Z%x0RxP4VOhA1BVxLo;{G7_D;qX!XsYCSMsvJBMYnFts5WoKBxUZT3OAk4yYe zOPKq6LP$`lQYGWYi@0YXetE2geP*Cj=f;g2n>|+%E4!RM(}foU9YKrt5aZ}NM~)n( zEh;OCKT!h<6^w+SnY=nD2p7w7AC`Nz%a<=lj~+dcKYxB>sDb_9J}dKc|CaD@?76?p zKs+&MeRiCf1i$=~LQV)}7~q$Xehr=SIBm7+(9Oy#V!fP&OIstyPOz?C^K{$e{sk4v zx*4S4Dn0|FQG4Bg{j<~hwA|-bxhh{;>2npa1`HWtojvC%GPCCk3^+n!o!Vz|AQZil z%TTV&CwM7x6yj!_1XJ|ZtXVU|6_8V-D6K?^66T%jBi&L_6k5J~d9w#uvSdkD8q;;p zig$9fgoRv+iaN;!agR&gF|mwZ+sokaEXo;59FRi*+pN?9-I&*ONf)%OBDiQ@ty5+Pmw|K>nbD< z+}C3sa^DQhqiY5CTbV{KOhuC7QN!Y%D@CM=JK{#UO6KQ9E}UF3MVK0Azmxlgm zEx}M|3NqfVFUx<$WZIw+V{u-L2t6~89&AUTP5&R@TM-*!!sU5gx+I@gT=c{As=RwjDxg$3_T$)&b=Gw~d?8d&n5`gZ?O0 zGPG&l7zOj^{l6YCFDC;pCwPqk8VI;`^OlIJV!F<}d7bmkV$Emk3m)GIgYy?Iip|{_ zShs0A6335&b60K}V+F;F77-Z2d--F1v(`gqXUTJodYHY2L-#%X;_hU0Hj){n@*E?D4eyO0hBtAqg?XptJ+aGl>*cy4S~dF8Iu+ zc(B*!pJx(Mehl!uqz1D3N*E!MY}as+?b{%coNpcd?HuGxml938h#9;V4e({PDu^Qov;zx{fbLg7 z)JL3ff%oqs&-u;8+aC0zkaaTH-}$WL&NO{?&%-pYg1t`nvhMk{PR8rAy*%T!4nErh zxkg?jQ0$}rjx~?gXMZQL@oVs|U6$uZik(PmoV|X|P415p_vFe+Y`BlALHnuiyc1?# zOye2Je*OBHInKJ=KaAY_r}Ze@?&-yg7Y8ryOY%b^po2pw0~(`&P@gWCH*ej>)af&^ zXvJz-aKoYgmsQZPer<6i{GLk`b4R>^RBO@!F(OAm%lb9(QJU1qm?6E07G7OU0s+iW zTjit2o(d!{UU(`I1a?Psv4|D(Jv3=lPZSm6pwqyyD3Cb~rp#J^qe8ITwP=Fm$&-1z zkOnr4!BIaKOZNryP2g}}O!ZywtlR$V)w_H9_Wbr|_k7;HyO(i)*K#BXyc8c?1DMyl z{94Yhci-w!g5+I&{9vc}v~c0Vs8_F^QE-y|MOSv8>SvnvPd@_u+CT5=)%uj%R`|Wk z?*i114-TR9Bcg&Mn+`T7lPG*(uG-0X=Th0iIYsf#Oe0w_n#Ny z_dotblg|C|Ui2s^mM;e?mMe{7pB6S>rdlui#Akc(%f!+$n%DlW&swME;XBjp@0!Q2 zcdgrAhds`{e$%s;ZT?@Ht=|-bGQ1GWIYAZJ2f`FdzJ6;~{~WP}Xm%ew2C0)J!QTCc z(56c-^z76Yd2;78Ai2XIRB#4mJDlef-*Hb+2}Dq~AClVQ{IJh+caAc!VYRpIRa+GC za!-$%r*d4wjF)nhzLjNt0o^~XPk$$VfkFGlGQI`Jgq?2~3Z`^9f#>?6?Aw13E!%g& z`fYz9`TL15Vo+~Xkz)m1OqAPHFgZbD$tHI8?AcJZaBfT*-XHm7u^lveB8ChfBO;Rf zre2C-?UTwr(X=k_?wXhB_B!~kX?kb6&O^r8gJ+uWlT^!So%-%o+pna_i?*qErrXOs zlZE;G1y566;lhX0;z4pJOoWA@OzD#NW<-BHdh#3*Wsg$S5YehjA1qq3+$e!CFUR_A z4LUv~lD-`gd0TN1+WJMs7b-=GAqI$10GIvT;OvLaqkx9N;V1(dqXAH#o%sE>X$yvq zo&b*vS5T&KUUX{T8p%HVz_h|e*8NLUi2$k7-MjY@PsDmf3Kc*;u|GOu%tQh|g15!s06-b7N-%HzbmapNZL-n)wr#n?1OzkFt3 z9ujQ^u?i&d_vHOO^Ui;Mnbh_@8oCIJA{+Iw1xD>B6>(0T@({z&H;_Ephe(hxf$^=$ zy7{r;;c?m+eNHHQffQvjp4XEno=$?JMT=^bL6T=HB6}N=JYkdcpbCUV`%)lZ9{C9B zh0)_DucPyBmVsB zA5-0tqsK6C*k~g{*80z!Jtyk!yY%&yyS7Is$m>PJ_t^0h__X3z=+%FSA#ChBU5L$@ zzYu9ZN`w4W8>2+kI-+23*wn**(-3*8l>A8e3MmU!#g3P-P>elUG)@N%8->4j?!uF& z&rq{MF_(vf*IzbX>4KkMbR5~|OtPQczgHK0B1&3^Po71_G|91aly09^KILN?$5HQo9aDQg zds}?wZ$K}uiT=IxH{|1b`m#}nqjbl9Furr(IFas%#r=_ECg6vk)*(W;Fc>qi2TB$% zY8DX=D)V?7qz;*pX0dtnSUexNtW!J}O}2O#HLyDd4j%_{S92FE!h&D7VD*|g$d)BD zHf`OG8ZEkF{x`$SQ4_+$iE0nmuir$xII)mb@-xtt*O{~D(V;;lOqe_!JCB@#*qap( z0_m_t>dTv_8jc)W3lH%iuzJH*v})#QC52_zuHT6DLR^3N{#!HQ?QLn>)I?pdY!%X{ zNrCh!lVa;%dmsiC-1H^nEK5xJ99dm5fN9L&OOnkT}ymg_!BcOilg9yHz3>PoMwOMGD;4*n=^paVB~z~M~{6G z^zGfA*Dud(*Pc(`d1tUM)?PhpKIZlAIXKenZR(oAeiBB#v_DMu=~>ggy4&OU?A5!* z>z!@!nZ%mUcEaxdu4UZwFrNndv&Q-Lu5P(}%I^?LpRf}|X`HkiXkM2tU%}vEBk}Ez z8<0Z?!}#F?kTXX%0|_TNCyieMHDS+RzG@ENGJp7&`)~A_c>MGQ5rgue^LrT8y$#CB z{uo1zYE=!#w^4`}&txiGOw(tIPw(D);1b=ED_yp3D}G+^EowDxgKq}+KnWr03^l}T z+Yu2V?%A^!)RW1YE)`OUc$DFWoLk%@Wpn5FPo>->xc!SPhl{rzI*GI?KE#mzJ>_W3 z3Y-*CYg{=eHEG6d6f0K&Kd$`QsD!6T7zZg+BsY$em;baH+0rG&hTk@e3!gpMweJur zS1gZ%!f_rtaRy6fO)^J8eqH|?2929yxbIuH?-*y+ZXg8dE6-SRoGD&z*Sa}k%h}Gc z)8-&=)(nPVwC_0(ZJN|YtuL!dpCh=&B}jn`z{Yf5@f;=1w$UXO-6GMH8uuzG86k(M zcRuqX5#>cOEzkFnxGE8c5Jce0a8%5&~Y-r_ejo^++%i@%kZSZX6>R;B04cP9OIeUz7UFn7B z{D_>Q^uwPt7o}Tr{ABmu{U}?~lhA2hY)_xLaNm%8+e9Qv&TY_$@n~7+3w$7!M{eAG z=pvwE2;6W7t2S)ICk67!WPE|02ajQo)Sn_nGOyT4BsF zm<;LCBDw6(9yoNwa9auE#SvosISz;o&$H()U{wF^$R-Ywcm2Bum#$pJv~ff6$Bw@Z z@$cTgrTF5^j~`e4jB>>a8jj`A;iH%+=TYlaEr-AVJ%Fo%tAq*uJ7n!6V1by(apqX^ zBcVy7`a&#YVbGXK$ekrU3X4c~?BtnZBl8Sew`gkipml!e7zH9|0baHCm1hRU6&1v= zxEUL9S7i>3b0K5DcrmZu`OJ&ydbcm2y143H7iqrRFRvDIjng~x>$CfvX`v->9k5?s z5qPHC3BLW=J>ozEKo z^<=Tf=;@xwWU~F!Hs=4Zn6y=3O>4zUlhfL@`(Kl5%C|Eue30Gh(Pxm=q*VtCY4Ta$ zOqp)o6Z?)LPPPUP9VO*bTX;W*)w)AhYtq!|R;B87T<~c9tW2K?5B)f5_+{;St8&eT z)+X`3QKU>2>(uEpn&i6wBmOvxl&LK4m5#gO^kMfNx-d#)wd>r&LZU3z@KNI|;Uo<% zy7e9)-V~l{)?mLY4hYEF^}ktpiOa^jUf~O?c7tZtfrE!#aN6^`B16H0 z!cE(vwA(7?++4OtLjm{>h;IP~#9)GP%f+tfe6)AVF=6NJCq&>T$RN15v}x04qUV@t zxOV(1R<4*RqR>+2>xNpJ^g!X4AcJeloH=t&6l%ta=qfVOr%fY@HA#&oYxL+b%pPU@ zcyV3TQH*-@=rLRo8;i7`NX^hxDN~Br$d%Q|S?!(QKQ5p8nMkzaKwQY1GTuoQ)+6g!OW1`!lLmxVLrA!2f}3{)mM zd-j~sxUJv3JyL#{7&X8A!gyG?D|R3+iU^OQ;We^8*nv zS3deEHF5|?9Ye%t*JN+(sECPkiI|&QLr4jV2g0*+_rIciH9`zbL^pe_6hUv_y&vNS z^+buHMVQ5Wfrm!U(g5%FkA3Immk<nmqv3WsjNSTHo*%()y?H5N?ItzjH_D`MBUE`sNMiGWYBPN#vPOZeb;{7y?amQ&Q$ao{T=2^=p*k@&nv2|j(>zK{-vk`i;d#ADEk2i> zw{WQ(iqB<;q;iu!mk9V&pK#?<8X@tDNOBDRy8<5GfT)rEB8=SA?bN;%CJ*g|+BG|i zU&DK7+oCa&iZ!2%c|XVIE!#|Unm-`gcIt32@UCJY^yoH+(h*Vu_*f4AFJ1m4zN*m( zMamRMyXFn>S%q?@SDGg@b&=L=c-D`B@V^bgH{u#n`baqWaY6;G#`Wu%BO+ha>4JXU z8puB7=SG36h!`c9Ck7=deO4Y(1jM%xbUJ~8#RzH7_`-Lyl8@tXGv3}NR9+z#VApt1PCvE)LbqSD>9_2c21I0boqInz9y+<9iZ{1RkO?V#B2nmaAAap-Ks#GFy_{dSi zInm%b197H`Jg?i(vuCu#VVQ%;VZ!j>dKiS4BOyzcuE27!@ULs7b8}ChiPae4uA@ec;@X3LUASJSoP7@M`ggCGx$7+)W=st3+XJWN zs8F~5!;mI9ts3<}+BB(66N=l{Y0oL+VBlTEfI}z)aMZbI*%$I{+kZ#tdTnt3=?e^O zS_7Rswl)3-?TcFgHvd{x`2>q^X>8Cgu4H~*8teqkr!>uPziZoKZ~(XO+((}to$Sf4 z{k&z{?_#-UFQN-4ylc;X9P>CUK0BjfrkzIALRHj z5gOP15)JCsGMtcoQS+jT0F7c>40d8>Z@>U|@(o8siGY}+6U#4Ga8oS5tlO{=?T1c6x%`>Yp>;D96~zjMpcZ-Q z0_t5CnJ(t+@Lr7f#2Sq_3zmhJalGhtL`>Nkt3Ff+6oN={P zs9=6qeYAXYs9i37y4{ z=|`zjqF#>?z}c%}9R4>$*n@gb1~E#U<{S(J2nHNN`37jTFY?@{8X|j<=Pq2txl312 ztypgK>)FM{WpF>sE{c4EW_>KGFp)E&+pKHXu8H@N>}HRR1$AM(E@tND%v*p>+x{>f zJz6zyj8rL-GYkF}ACXgM%o0~r_fV!pQPlbBOS3=6G!HSxO+yLv3X(cya-%pBUkr)U z4BxJQ_la-Er?`CW2Fi(H^}?SNFvNC=X!~y7`nz}#d4UtB&*8JOC6Fgq&i^wNr?7L6 zaL2B_s8FhyAwnO=iH&}}x*6i6)^un{V6*gZ>-HUDs_u?(eOXbyOew?_N7Y&n;k5_G zv%FZaGv@6wXU=4ji4z8n4-GHh7To=@<1a%XFN)=%H6k4-kU1?fX2>A>erK`e_rK7s zLu*4MS&jx5_Q_r}2`SCv1!rI%*q^w9vmRaAn|tNYs){n%Y>3rhRFaQ3k8_s{M?7x$ zK%=-6obWnrI~WKJ7;w%}zUC{LU5=YvUB%tZ2N1M*k<(;*Jw zP!}6kEkTDiElimkH*cU`ml615*FMzvq6)UH{SlqpwGtORkxe?eAPxFVe~cSfFGa_; zEe!|xNW22jrIZp$5`tl)$D?%J_P8pXYWj2^VT*V(Y1g%v@f5;(hzsum59=R0b=rtm znTPqcZHgo5ZmZkCk;tF7I;vJ}hv`e!qgatokVJf5QgjopH*X@|V#Jp4@QGNuVii70oe~pA48Vu)C&o_EjP(gmK?$?d zmV<$ii~)zEe9h4z-D_08HvVbdQJfxsjG6NmAztj5xOnZh5TkHL3`>LLT!?fLe$A%* zR7~5Uxb&OJ-y)1~MqiH^ZnQC5wCy79tfph~_>snx8x5ED>ory!9d0mRsDyJ6?k6=@yKF~o+nI@5L*{d!lZ9!q5Ggw!c{&)zTCORg;H$OFNNE?P7-%2 z@?;+7e=0r-|JnI3+SjfiTvG?p@{B7YcJ ze=Z??ZcYP`n0*Z!<6)j@S z3-<2b85N2bfVjHC`{H*qew-MXBF60L4TU(OhZn-BM2GbRcOWdhHg4XEG7UPRk7!-< zs8Zxekvsm>u z*u8EJk_eZ0;-AC9WllwjVny*$npDPm3(o?k6W3GU&0T1;KuJK~m%I$mh4^gwc?rIs zG)6eKx^ggof-$2O_2r)2CNY^tH(fQWe}Upfi{OXF%TTvv7p#~wMcisVGaO%Ku@ZB& zPyvYVO_ZqD7TXt2L-uT0O#R;dT9Bm0`AN)$JoD~V=A!66++ zYw+Cngy{phvrZy9t|xsvcH)$9nR#R`09UVGmwnPK@}4hPIW&8}w~9ErW~;8~-k^%{ z@WPL%zJo{L#_fCNBafnF2I9pD4h9?yc#nZ_{Umsw?4)>w0WK`MAaUXRtbA!?`#1x( z3YXNh)ewAAFpDVAWW%ao*Naz+TZS0${GFEZs?Il$BL^WIk(iZB5-&CuF8j$S)(r2~ z#t^Oy>C@u92+#5J+6^dOycoW!RSEMJEyqS7aI4p>#j>A%MtmU{mBg&xlgE!Rf6)s3 zCfvw;$#YJ$SSj8-dhRO1hqcUc33}=ihW!T*375PJM~)mr1lc$2+^(h6m)@Y#tHuA}!-weDtq=AbIE-XT z5(=mKRQeF!aMTnbf7zrxHf-34KaQNjy&Knz@5~i~mv3jzG5eNH8rCuUlq4|KDwPuv z>^+F%WTWXzt~Xnj%qE=xc-8)!kFn1+*yq=F!Bt%$lC>+9GFaf2GP*FhVq9 zGYOu814f^F;G{Yj2$>jg3=Ra~xLg$t(>e1NqT`S$=-8mL;kd@lT7nVXTAFi|BrNvD zEC8GSzpA)!>*08NB2BdMd?3SJQR+E%{J2qoxgd%<*+hF&l~m52J8ur(^UUBg;U+0! zCBf{}d$19E=E7e+4)s$*mggZU`t&Vu5B9illpF(| zn^bO@q6xD!9m-+40PK?acq7$sGnFwyFoR6rg4A#^Ms29!r*t&*lpIK zCn}aKY|edd6V7r#-!YgmYYeJ?Ue$GG)F0J5Sse_#(-?3FWdIGoDumEKA{VCNW51F8 z%iBo)ej;q%eHi2VcEXpU^%)ewFN?Wf@^}TSCjmWal$E%T89R0yq)(qth+%ZEvIlCM zy$|+h|1`>S41VFR+}p61bx-pT3;~&!`{o^c4nUslADiPWt3+CGUFP8cv266|%k#V; z1=fJ7*PR*iX$T?OhF_O58e|^%YI$kqof?-X9$gp)|(K# z07ms|R~H)b?p0^){otNtzagVV5h<)(5pl%h99qi}7aEI*Cg&;b^U|xjoHz#q4hFo( zfI}#~$ITn1*cY+GM~`9JjPJ2w>kf0Su}6oNs8H6Evs97o8#NXR3fu7!nBZzVG=5H{ zo7AaN;+h)y-rxT zc&UMyqDd7khOjQY0uKA}CU7b~Ua2$?UI-@x_lej4w%I(>(?_TNqg;jy$mvr-DIf!J zqiy@OUgjrWc+ql`;>fppv>e-^AId(1M;Rh{`ob0D5X0@`M+`!-PaPuZ*Ku(2IT-K_ z0}i3|4MT63&c%-l8?D^{y@_ z-ob!_0pBs;7##2&M{k^ii?7G76NjY5P2uM8 z5sEL%mM&$i(I{c}304vz5)huL{CcXGw-Y1r+%F4?@LaTy?Y3cQmX61B=)S9`7z?jl zp-g~+v3}!b#Fk?uG|sL#)4MjLIO99T|?MpsBlfckE zYM&94%z47NapJh*y(*{uR1TCz$fqq|i)Ib#$VWl~W0ajH=OX#CU#RtXhZ9;m5|t=J zblm&#;>B}B5p!(}okMG*OJZ=28V@7k4D%1S;GNrs4#BI3>iPiM+vz{kYXr~&P~ zvMzf$t^1V6X&e@-IeYgXK#@$TP_AYpBoM>#kHwbi^l?Lx`QuE+5W60!Q5bl)!_0T~ zN8|MF>5;|rIF80KF9V5aq{yC%JH%yFc_v)6&J!<2G(*UkkLB!T74O;ydz$7~+%k{m zqvfJ2S1vP)t8N-Ch$MJp9+LFCckhaw+Q%lHMjt31roCDX9;eRWp_m;^nfybIGIxcE zFVkks#K_6t3o(9%Zmk-j+UHfwd?B0+3Y&Rx{y7+MFc2UZa0q39pwWMon1xdowuIux zu}jCc#0*c-S;DG8>nDEp3Grv%yt#9kJWr*)2*O1&oIK?Lx+^<)_!#Qdsexxt#lPmC|6t?R z?U*uQG+K4&h9P1DI7W0&G4uo3r<^%wo{W13@f$fERV#mH>fu-@k))r`$y2^XyEa3R zwO}R;?bQ*b#99(R6BI5}$oc1Bz`;PkV?cgnP6#Fp$RcQ37NTVhA3e^>Rdp3s^+ZH6Vp z%A|{#IV)qn5>~35pIUJ<}=w8{Q0+4 zt5GXU#K+dU4I8a5YBjJnY}{R>x0O(YG@!wd+82fIlW8bMLOPfTS zND`uj7Fy9RRJ2JdrA1Ll*(y@@k|d$*yX^Zg1~ZH?X8Zo;9aD z5d-^R+_bsi@my8K-fM8>in6z_fN*NZQY=4Tuy(8~&!3zqhfe?X&stMvA=20@N9A)OPB*N2RyE2J^t9k#*q5R(chqKiK3V= zWg0gBw++wtdCCwQirIvNFoU*x>N;qikTV+(haN@KI#qCQ#}4LP$(IKWf^bk+v3ec) zJlxeBxkO{)6u(ZLFb17FwnN9e+KH%en`q&N;EjPV%Gr*kQL0o4{2>E=E^{)m@Q_6d zp+w;V2okN`%A$Ot?$5uL2qDTV>95BlkN3fW!$?i~UmBBwNR$|)h1=ulfve67(_Rqgq@yABY ztU6y#Nx2EdWbKcHmRnJH7A74r%bPd1h$*?I2!w=3;{CS<8QZ5MQsv8CYmC8DJNKhc zUcfU?_BN-5?AW~*v;SF--xn>$owwg=4srhK^B+ZvcR5OxENP6T|MKmZV(+#n5+uLU z#fzfI@l$eaS@q9_5?ghR8oDI7Fk zf4MJCo<1X5u?4Y3lwqEH_GR#F!Nf4B8z|sr`a_39ar9U?wr$&P4wmNmiebl3$nQ8x z%bAfpL1fgJud#IHI&+Bcq^Z-*2_jS?3JE=it=qQYq$r+nz%E}d$W(wFW+CC4+T45t ze*bfctWVB12PsDgvE8d7#CqV!5!U2bDTxu*NajOUwY^q&Z z0eklykT79Fm}?sDa{M>n8pH2T-+MbokNeJG#(IbqUu>53S=O7|Pk2}~9Tk^q*QkOa z&-6sU0dHcRtWh!y+m`E{Jlk`^SD$0nsCVVv20Z)HARLxMvgx?CIcwf;qWHAUJoA8R z9t_>3&p_-73Bxzv4#lGn_pn6Lpkx171hO81+=InseYQ47cLbDJ@=A`Rc8zLSzG4-k zWeY%l*+wyO+DruI%ZplK<%gV-J6EC4M?ym}d;UV)db1F$VV`5>ocTB?g!6@G9yfs;lH^E{Y&2*6e&w2RVE53e>D#%_#klix@mJ==1PH-BBv2BAPe7 zT8{5pg6BkpTTeu}<;zzv+hRT)KNXeAmNa5iPF(Ko*dD!l^)|!8-S@_e`*Hs6QbowuBUXVQbzNXoDT^YMXSY&6*FbyK+P>2 zHH5(N%tdOiQcH93(tj{^)C}}{>3$4&p)cZOt&bX}6s2z5wAm2x7R|1~RDEM(-Q5#y zlctSz!p1fl+ji2}wr#ZGiEX>FjV3vy}-}~Zzfgkpsy=KjtnLRJ-JBi_X zjGNn71Y!jth*1YDWMrar6G8cv;49Z>E2GGTTe6b^vs%06?}sJE7E@ZqPEO7m_;2>( z^mqrIYEjCD!M6c&mZJNme;=thKV9u6BsUc>kKM>!D$X!PWalM;l&iKgLUN5xZ0xhn z;9xuMXb4@(KV%b^mA2;e7G+7osVNdWPI*A5)>#Nz?tTwIwXM(Q%}kHBPRMAsW5a1j zZv`038)3e`A`kd;;ZAls33%Oi=r;t)WN@x<(7>sfMI|qd4hAO>au@>iJlr_L=PLu2 zgvb?zICtJA1#BBUHTOH8?~GTUqEcVB5o}hw;Cqrrw!duttxoVwc9GYK41G05t$_hg z0Z&#m`!=&?4ibus#=}JP@_$*xO67V-R;kt@coq>>N&tzor*XuMpSmDS63+8bcOAch zuIrjOhf~K{AmV$e2KR2ZJKF_bB|WIScIM!civROmVXEQGRGJjc9jq%A$KC=H8+k*@O|l;suHAy$kujht{?YBdJ0d8b!f9MN9kl=4BJr76H1lqak9^!$#B?8 z?T-e0_!{HtdGFRfoG(83|DKSR5lSio>H4edPCx1Uv3%K;2H#Y~ycuVH;k3SH!MHae zNo2W`F=ya*wiM`1=n0aqfG`iM9U5rh5i! z!d>Q=o|=wugF|$9L@;^U=qXDbnJl)=d7S4OOHz2i>Swgze7qG2Zh&~3Fz=kdIJ2}` znkWx()iZrY2=Tq3p0i&IuQ#&E^-K`2n|mfJqtslIg8gic^S=+Lhjfej*Zg2c<%(}Y zt+k_Npo%)Ev?38**BRVu`F;M(Q<#u>SKvV%K|1?l{7 zrTT`{d>ANKfzmJVrW1(;-5GqqqQprtXuL{%Ji;h2Kc8B!4EfvgdzC9DM8uzdp+A6; zh`{#wR;SzOzdSD|BapZQKNR#+5pXa$zmx)|fedp>sZ zD+I0A?bjk3l#oD5d{}dEW0Fzi2Ci%x6E2&{YP`14NHR6~Gd&?k##}2BCK_%+MMVN) zxW6V=kJLj;$jc#?{^mJ`wNj;#;?|`G-zd!wT|St}84)xp##&>Ao{^UC2q!EK%rrl-dhs%cGbFhcQuBdkwUV zrt=;?+vVK*0=`IHb;OiAJe$ZHq76`nH>tqu6>Y_83r1u47vuZ4z)~V_BYn@>oW-}E zo2X|EnipY`sL)Q($#}U=E$`E4S_aW=uKGTpz7b$4_XsdcK1Sv-0!h{yhWK zA(#&Ug6gyZr~}C(`kYB5e7JFz-OhxCxa{MhA-bQ8^cy-Zy_oDKt`X5}N6ZrP^_lw} zs#3hTcU?i-BxdKV1J{mJiE52Iqw$vjb-t%#fQfXHI8 z!Nk0bACj)+AsJZj&Uop>=JVGbo9bjR4^``J_*U<5A&JdTzz!8vZDVp&zR3t2S(es- zVju-Sx*F^K% z?~JLjo!47Ldn{dyz8(kt9+)_57x|iBha$&qj#tf62SE>s`P1fS0D0nK{x=_W7z{+_ zP%wfRRrH=Lcz+Uu$M`edNjR{2x{PCdG~v)iD>tE_>6l+R_r;%1IA&i@Wce5;<8yOs z$?d2;#i){ndu~kT*^ds5{D0A7Y7;DQIoEMDY&9@C{4g5Ap5Gc86EZeiL-qam2m~gd zKa0VA%}+o$in;Uo6uhfJM-G_+L1tNHL=u?oQ3f~Jfk&xTIeGgnqUNEHHo!F0iV-l*5Gsa{LdW74B400bde~|R^}$} zrlWguCaZOXG%8f~(C*0Nxh+Q64963?-ystG-l3fbd;*TE ztqp%T3|z(*-qwLb3nvuFxh72{tBOd>q~oL=98VbM`)BU$Yvj;;>3*lw%Vm9!s&{LR zdOP2%J$Iy=xvci47KbUL8=n_VJWdyE_%2-2$kgtqx$k(e@Ppys)-^~hc_PI&0Aha# z>Ro~CHqx^swJLTcKCMIz9bI2uwv3rx=ld6G?6UBtlaO7l=b53D`-&3hicYH0zHEto zzA^4j3-ivwk7Q_W21WShFeikc={^^jPqu&e*pLyQp8dA}V_;xzoW!A(*g&j0MLU9W zcAl%$8F^#HvwJH|NCcGtKSHSUtgqfTN1Wf^zwhVM@SA`_D5HMaUoJ|RP}AeVFa`0= z&PfX67s~1a!@wtSfjW{NAt&Uz98Ovb0X1f9rXz!@j!h@{jM74SGR%kTpWRZFhz>&V z6Y2A>_^R_HQY+(}``&Qrc^>j*^5X~V#oWoVm&prN3^1WGP6^2Wk_dzdT>lpRN-R-( zQNnbAJQT_i(iOw!^!KV)Okjh830`tP(izxmRBnpeQh{2NKR?>`_^$BM;->lANQ||+ z81@%Ua5H&_Stbj=X+DKq)d+Eq+afbA|es=Z(V?qdvzKD9`o(jCSeQ!aS^MhksB~t zN0MEn+#5e#``0U$cBS4u2k#dj9HIX$eDP!|@M?E-^Vo5HE8<(X@sdJ!u5UX}tOSzI z9ITjpbvu3c2QSC}5GwHHQ{<=s2;>Clt?b>!OmOJKQR>i`8-U}QmNE@YP<7;AP>j<(}HS`TL5V_vx1Y*ZNm?(trQU; z^e0@;RzeaLf}D(1@%zze^@Y)UoZum3>f51q%jq2Zz>}=}ZdHR_t)pWqV@XAWwKtER z|2Dpk`)VT$ELRY8`+Dg7PU}1=-x!EUEcOH*48I%k5?}@*(*;&v0QrsLd z$7KK2C&YvM5tKv*J&;3`p`jr*lNaPtQyV5D-#n$}Ogd2zTMCNmF&!9)M{&U}oALZk zLHRTbw6{>Z{UFLYYU%7JqQn(kc!`0yWAR(B6bH}dpNmtT

D+XCvL^C9DzEhQiAH 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 z zuW4V(MkHguB|n5!yh$jp)%^&6dkpLnN@5Kn2%^^mzCH#_piE+l#(_!r(%ocQlzcyy z9^Cf`>X*gl5%p7+V+Y!v4np}~jAx7)f3ZKJFRqngYye`?`YQa)?A6tnFLu6rdC4d9 zAojiZ*TRCM=i&>Im-Zl(*D(Cp@LfWQd$en*j|v`JtE04w7|0~Hn6qa>UQBRffV@JC z?v>|_MPH3(v57eCYUDda2p;Ogn}bj;_%u%5BUq=LVSoDc>4V*6DP?#<_-?K9z1#QP z{Ol+ZNt+bo{($#^flWf0&LuXmqjVsBz{@ePHQYNm=(W?I;n+V@G)pY~C-AO+uOM?I;mS2j%Y- zwusQzs_UgmdCC7;J>OH?w(9!Qc5UUg=cgQm6445${g!1TlyyCzzf^qWPIeCmN7z0F zG9EH#;@(g7{z4H->#noXNH*SIEx*4S?Gj2~u6Y$8gffHhHE6o`0KRvAFKrv#7}$G1 zaqLpmIp(y-FXnq`+e>kB&ve9~z6eAp-x^3zTt^|!3!%Kn*njJKzqkC2UwC`uIDTYV z_r!PVZH$xT`5j~Ko)~^zUc0MK2xVP@?NTh@Zvh5Ge)7)%SxsSf6?Lh%f9_JTSSeVXj~tpqOhiK*-f26hH_;%u4H#GgIs_RL8rQ%3S(0(nlD@vh~a{Fy{N zITJyIaI!Jk{lei+(?pItc5c5}7j<0St6#!n&UUc}e{aoUr~J~E(|6xyTi$ByuWkLO zxr5V}wrd^dYd+V)6aUtDi9gNPq)k41c?k0}kK8aYAFX57rUTvw26hRhyGkw=m*$Ut zvyk}9f{~8h>ip@Kc$%uy5jA%*4F;xPX#zR>#m)=?G+-={%C&G=#7#c#Ea=Sjmj!>u z$->VxD5slU{+Ik_SnM&k7SZ4#CLF}RXKsb@n+!mRfBaT+G6`!K_w>VTU^McxbBkSA ze)`JS&!s0+Q(gt7KeGvu9a|~fZmET_M$FI59XWMT*HZcQ;fEi7=$GnAIN;;83-De8 zEL23VpFe;8-~zfg9Mwgc>7SW?%TM^0|LXS~_l!I3vo5i}6hALXr!MBVc{)w@>7L=! zo;cG_p4>BU?)+w&nQkk5#@({VZ_Av1?0dhIJ?qYo+b`<`Ge7t?-k8=ZYoiScg$0y} zfS2@1#=&9U2LqdgQpZ>rh+OR^OA~+p?o;ITTs%B$5zRtsG4U&_K;p;bAA8KiWoB?sKlT}yJ?>15Ot5wG#2qt}D(PazKa*@NbGlRZtVis7X4;lX z%S4FaSvu}5Gkyt=J7($@_slL-fpKll1~Q)0H~jU1cwo+h;NK>pl)QiUN?sa4@8s_b6?QsV=h-=wzRM;w)Kt}r z1^siu-9!$g!@1dK$D7E0nfT&l5KRIw{Ss&TCGIp6Z<^Dd^wV$JuZ4>XX1rV6;shtb z$8-JG^wXWZrr(}j*c!g)cPZ|gCJVq;{JCh%c!{eG&jISi>b=6=X(LiQhD*eT_TAR{Je(aW2JE z)&Wm3V9W$xPeTsZBQbE1P+qEp#p-}>8v|c!;=Q!o5SW*y|F(PU`>K8VTC1o1`@+J1 zTWh=@WBz^g{4SxK1NG>z_t9ZTEgpk`Irbk@;y=HXbq#^z)$gB=g`B${{u=tgp2a+rLzD4002ovPDHLkV1g8U9+v Date: Thu, 8 Dec 2022 10:06:05 +0000 Subject: [PATCH 17/44] e2e: changing default relayer tag to be v2.1.2 instead of main. (#2903) --- .github/workflows/e2e-compatibility-workflow-call.yaml | 1 - .github/workflows/e2e-fork.yml | 3 --- .github/workflows/e2e.yaml | 1 - e2e/testconfig/testconfig.go | 3 ++- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/e2e-compatibility-workflow-call.yaml b/.github/workflows/e2e-compatibility-workflow-call.yaml index 7a697363bb7..3e438ac0026 100644 --- a/.github/workflows/e2e-compatibility-workflow-call.yaml +++ b/.github/workflows/e2e-compatibility-workflow-call.yaml @@ -53,4 +53,3 @@ jobs: CHAIN_A_TAG: "${{ matrix.chain-a }}" CHAIN_B_TAG: "${{ matrix.chain-b }}" CHAIN_BINARY: "${{ matrix.chain-binary }}" - RLY_TAG: "v2.1.2" diff --git a/.github/workflows/e2e-fork.yml b/.github/workflows/e2e-fork.yml index 09462fb4e25..8100a64a943 100644 --- a/.github/workflows/e2e-fork.yml +++ b/.github/workflows/e2e-fork.yml @@ -46,9 +46,6 @@ jobs: with: go-version: 1.18 - name: Run e2e Test - env: - # see images here https://github.com/cosmos/relayer/pkgs/container/relayer/versions - RLY_TAG: "v2.1.2" run: | cd e2e make e2e-test entrypoint=${{ matrix.entrypoint }} test=${{ matrix.test }} diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 2fde4088392..64a459b9415 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -46,7 +46,6 @@ jobs: chain-image: ghcr.io/cosmos/ibc-go-simd chain-a-tag: "${{ needs.determine-image-tag.outputs.simd-tag }}" chain-b-tag: "${{ needs.determine-image-tag.outputs.simd-tag }}" - relayer-tag: "v2.1.2" chain-binary: "simd" # on regular PRs we won't run interchain account or upgrade tests. test-exclusions: "TestInterTxTestSuite,TestIncentivizedInterTxTestSuite,TestUpgradeTestSuite" diff --git a/e2e/testconfig/testconfig.go b/e2e/testconfig/testconfig.go index 524d5c417ee..544bb34ae94 100644 --- a/e2e/testconfig/testconfig.go +++ b/e2e/testconfig/testconfig.go @@ -36,7 +36,8 @@ const ( // defaultBinary is the default binary that will be used by the chains. defaultBinary = "simd" // defaultRlyTag is the tag that will be used if no relayer tag is specified. - defaultRlyTag = "main" + // all images are here https://github.com/cosmos/relayer/pkgs/container/relayer/versions + defaultRlyTag = "v2.1.2" // defaultChainTag is the tag that will be used for the chains if none is specified. defaultChainTag = "main" ) From 0dc8d1dba9964b7fa2b9d6216c6f85917cb09f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Thu, 8 Dec 2022 11:29:07 +0100 Subject: [PATCH 18/44] refactor: simplify optional tendermint pruning migrations (#2862) --- docs/migrations/v6-to-v7.md | 7 +- .../light-clients/07-tendermint/migrations.go | 72 ------------------- .../migrations/expected_keepers.go | 16 +++++ .../07-tendermint/migrations/migrations.go | 46 ++++++++++++ .../{ => migrations}/migrations_test.go | 31 +++++++- testing/simapp/app.go | 2 +- testing/simapp/upgrades/v7/upgrades.go | 9 +-- 7 files changed, 101 insertions(+), 82 deletions(-) delete mode 100644 modules/light-clients/07-tendermint/migrations.go create mode 100644 modules/light-clients/07-tendermint/migrations/expected_keepers.go create mode 100644 modules/light-clients/07-tendermint/migrations/migrations.go rename modules/light-clients/07-tendermint/{ => migrations}/migrations_test.go (85%) diff --git a/docs/migrations/v6-to-v7.md b/docs/migrations/v6-to-v7.md index 9046ae679f1..64cd0d54f28 100644 --- a/docs/migrations/v6-to-v7.md +++ b/docs/migrations/v6-to-v7.md @@ -21,7 +21,7 @@ Add the following to the function call to the upgrade handler in `app/app.go`, t ```go import ( // ... - ibctm "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint" + ibctmmigrations "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint/migrations" ) // ... @@ -30,7 +30,10 @@ app.UpgradeKeeper.SetUpgradeHandler( upgradeName, func(ctx sdk.Context, _ upgradetypes.Plan, _ module.VersionMap) (module.VersionMap, error) { // prune expired tendermint consensus states to save storage space - ibctm.PruneTendermintConsensusStates(ctx, app.Codec, appCodec, keys[ibchost.StoreKey]) + _, err := ibctmmigrations.PruneExpiredConsensusStates(ctx, app.Codec, app.IBCKeeper.ClientKeeper) + if err != nil { + return nil, err + } return app.mm.RunMigrations(ctx, app.configurator, fromVM) }, diff --git a/modules/light-clients/07-tendermint/migrations.go b/modules/light-clients/07-tendermint/migrations.go deleted file mode 100644 index c369e6b6836..00000000000 --- a/modules/light-clients/07-tendermint/migrations.go +++ /dev/null @@ -1,72 +0,0 @@ -package tendermint - -import ( - "fmt" - "strings" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/prefix" - storetypes "github.com/cosmos/cosmos-sdk/store/types" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v6/modules/core/24-host" - "github.com/cosmos/ibc-go/v6/modules/core/exported" -) - -// PruneTendermintConsensusStates prunes all expired tendermint consensus states. This function -// may optionally be called during in-place store migrations. The ibc store key must be provided. -func PruneTendermintConsensusStates(ctx sdk.Context, cdc codec.BinaryCodec, storeKey storetypes.StoreKey) error { - store := ctx.KVStore(storeKey) - - // iterate over ibc store with prefix: clients/07-tendermint, - tendermintClientPrefix := []byte(fmt.Sprintf("%s/%s", host.KeyClientStorePrefix, exported.Tendermint)) - iterator := sdk.KVStorePrefixIterator(store, tendermintClientPrefix) - - var clientIDs []string - - // collect all clients to avoid performing store state changes during iteration - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - path := string(iterator.Key()) - if !strings.Contains(path, host.KeyClientState) { - // skip non client state keys - continue - } - - clientID := host.MustParseClientStatePath(path) - clientIDs = append(clientIDs, clientID) - } - - // keep track of the total consensus states pruned so chains can - // understand how much space is saved when the migration is run - var totalPruned int - - for _, clientID := range clientIDs { - clientPrefix := []byte(fmt.Sprintf("%s/%s/", host.KeyClientStorePrefix, clientID)) - clientStore := prefix.NewStore(ctx.KVStore(storeKey), clientPrefix) - - bz := clientStore.Get(host.ClientStateKey()) - if bz == nil { - return clienttypes.ErrClientNotFound - } - - var clientState exported.ClientState - if err := cdc.UnmarshalInterface(bz, &clientState); err != nil { - return sdkerrors.Wrap(err, "failed to unmarshal client state bytes into tendermint client state") - } - - tmClientState, ok := clientState.(*ClientState) - if !ok { - return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "client state is not tendermint even though client id contains 07-tendermint") - } - - totalPruned += PruneAllExpiredConsensusStates(ctx, clientStore, cdc, tmClientState) - } - - clientLogger := ctx.Logger().With("module", "x/"+host.ModuleName+"/"+clienttypes.SubModuleName) - clientLogger.Info("pruned expired tendermint consensus states", "total", totalPruned) - - return nil -} diff --git a/modules/light-clients/07-tendermint/migrations/expected_keepers.go b/modules/light-clients/07-tendermint/migrations/expected_keepers.go new file mode 100644 index 00000000000..6a0a57c054b --- /dev/null +++ b/modules/light-clients/07-tendermint/migrations/expected_keepers.go @@ -0,0 +1,16 @@ +package migrations + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/ibc-go/v6/modules/core/exported" +) + +// ClientKeeper expected account IBC client keeper +type ClientKeeper interface { + GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) + IterateClientStates(ctx sdk.Context, prefix []byte, cb func(string, exported.ClientState) bool) + ClientStore(ctx sdk.Context, clientID string) sdk.KVStore + Logger(ctx sdk.Context) log.Logger +} diff --git a/modules/light-clients/07-tendermint/migrations/migrations.go b/modules/light-clients/07-tendermint/migrations/migrations.go new file mode 100644 index 00000000000..401c695a3e3 --- /dev/null +++ b/modules/light-clients/07-tendermint/migrations/migrations.go @@ -0,0 +1,46 @@ +package migrations + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v6/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint" +) + +// PruneExpiredConsensusStates prunes all expired tendermint consensus states. This function +// may optionally be called during in-place store migrations. The ibc store key must be provided. +func PruneExpiredConsensusStates(ctx sdk.Context, cdc codec.BinaryCodec, clientKeeper ClientKeeper) (int, error) { + var clientIDs []string + clientKeeper.IterateClientStates(ctx, []byte(exported.Tendermint), func(clientID string, _ exported.ClientState) bool { + clientIDs = append(clientIDs, clientID) + return false + }) + + // keep track of the total consensus states pruned so chains can + // understand how much space is saved when the migration is run + var totalPruned int + + for _, clientID := range clientIDs { + clientStore := clientKeeper.ClientStore(ctx, clientID) + + clientState, ok := clientKeeper.GetClientState(ctx, clientID) + if !ok { + return 0, sdkerrors.Wrapf(clienttypes.ErrClientNotFound, "clientID %s", clientID) + } + + tmClientState, ok := clientState.(*ibctm.ClientState) + if !ok { + return 0, sdkerrors.Wrap(clienttypes.ErrInvalidClient, "client state is not tendermint even though client id contains 07-tendermint") + } + + totalPruned += ibctm.PruneAllExpiredConsensusStates(ctx, clientStore, cdc, tmClientState) + } + + clientLogger := clientKeeper.Logger(ctx) + clientLogger.Info("pruned expired tendermint consensus states", "total", totalPruned) + + return totalPruned, nil +} diff --git a/modules/light-clients/07-tendermint/migrations_test.go b/modules/light-clients/07-tendermint/migrations/migrations_test.go similarity index 85% rename from modules/light-clients/07-tendermint/migrations_test.go rename to modules/light-clients/07-tendermint/migrations/migrations_test.go index c7fe56af5db..b171b844f09 100644 --- a/modules/light-clients/07-tendermint/migrations_test.go +++ b/modules/light-clients/07-tendermint/migrations/migrations_test.go @@ -1,17 +1,41 @@ -package tendermint_test +package migrations_test import ( + "testing" "time" + "github.com/stretchr/testify/suite" + clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v6/modules/core/24-host" "github.com/cosmos/ibc-go/v6/modules/core/exported" ibctm "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint" + ibctmmigrations "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint/migrations" ibctesting "github.com/cosmos/ibc-go/v6/testing" ) +type MigrationsTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + // testing chains used for convenience and readability + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain +} + +func (suite *MigrationsTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) +} + +func TestTendermintTestSuite(t *testing.T) { + suite.Run(t, new(MigrationsTestSuite)) +} + // test pruning of multiple expired tendermint consensus states -func (suite *TendermintTestSuite) TestPruneTendermintConsensusStates() { +func (suite *MigrationsTestSuite) TestPruneExpiredConsensusStates() { // create multiple tendermint clients and a solo machine client // the solo machine is used to verify this pruning function only modifies // the tendermint store. @@ -99,8 +123,9 @@ func (suite *TendermintTestSuite) TestPruneTendermintConsensusStates() { // This will cause the consensus states created before the first time increment // to be expired suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) - err = ibctm.PruneTendermintConsensusStates(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), suite.chainA.GetSimApp().GetKey(host.StoreKey)) + totalPruned, err := ibctmmigrations.PruneExpiredConsensusStates(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), suite.chainA.GetSimApp().IBCKeeper.ClientKeeper) suite.Require().NoError(err) + suite.Require().NotZero(totalPruned) for _, path := range paths { ctx := suite.chainA.GetContext() diff --git a/testing/simapp/app.go b/testing/simapp/app.go index 633199155e7..b1f5379a02f 100644 --- a/testing/simapp/app.go +++ b/testing/simapp/app.go @@ -899,7 +899,7 @@ func (app *SimApp) setupUpgradeHandlers() { app.mm, app.configurator, app.appCodec, - app.keys[ibchost.StoreKey], + app.IBCKeeper.ClientKeeper, ), ) } diff --git a/testing/simapp/upgrades/v7/upgrades.go b/testing/simapp/upgrades/v7/upgrades.go index a9072ed63a2..078ae3cb088 100644 --- a/testing/simapp/upgrades/v7/upgrades.go +++ b/testing/simapp/upgrades/v7/upgrades.go @@ -2,12 +2,12 @@ package v7 import ( "github.com/cosmos/cosmos-sdk/codec" - storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - ibctm "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint" + clientkeeper "github.com/cosmos/ibc-go/v6/modules/core/02-client/keeper" + ibctmmigrations "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint/migrations" ) const ( @@ -20,13 +20,14 @@ func CreateUpgradeHandler( mm *module.Manager, configurator module.Configurator, cdc codec.BinaryCodec, - hostStoreKey *storetypes.KVStoreKey, + clientKeeper clientkeeper.Keeper, ) upgradetypes.UpgradeHandler { return func(ctx sdk.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { // OPTIONAL: prune expired tendermint consensus states to save storage space - if err := ibctm.PruneTendermintConsensusStates(ctx, cdc, hostStoreKey); err != nil { + if _, err := ibctmmigrations.PruneExpiredConsensusStates(ctx, cdc, clientKeeper); err != nil { return nil, err } + return mm.RunMigrations(ctx, configurator, vm) } } From ce4d77104ece211a3fe5794ea4649a06f240309e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Thu, 8 Dec 2022 12:29:12 +0100 Subject: [PATCH 19/44] chore: add tmsdkeys to CODEOWNER for docs (#2904) --- .github/CODEOWNERS | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 619279becfa..f53e08bdaa8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -18,11 +18,6 @@ /modules/core/ @colin-axner @AdityaSripal @damiannolan /proto/ibc/core/ @colin-axner @AdityaSripal @damiannolan -## CODEOWNERS for core/02-client - -/modules/core/02-client @colin-axner @AdityaSripal @damiannolan -/proto/ibc/core/client @colin-axner @AdityaSripal @damiannolan - # CODEOWNERS for the light-clients /modules/light-clients/ @colin-axner @AdityaSripal @damiannolan @@ -42,3 +37,6 @@ /modules/apps/29-fee/ @AdityaSripal @charleenfei @colin-axner @damiannolan /proto/ibc/applications/fee/ @AdityaSripal @charleenfei @colin-axner @damiannolan + +# CODEOWNERS for docs +/docs/ @colin-axner @AdityaSripal @crodriguezvega @charleenfei @damiannolan @chatton @tmsdkeys From 8ad5932615ef9b44fc61af40437a346b0fb2e58d Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Thu, 8 Dec 2022 12:43:02 +0100 Subject: [PATCH 20/44] docs: add legacy docs for ICA --- .../legacy/auth-modules.md | 268 ++++++++++++++++++ .../interchain-accounts/legacy/integration.md | 195 +++++++++++++ .../interchain-accounts/legacy/keeper-api.md | 121 ++++++++ docs/assets/ica/ica-pre-v6.png | Bin 0 -> 34698 bytes 4 files changed, 584 insertions(+) create mode 100644 docs/apps/interchain-accounts/legacy/auth-modules.md create mode 100644 docs/apps/interchain-accounts/legacy/integration.md create mode 100644 docs/apps/interchain-accounts/legacy/keeper-api.md create mode 100644 docs/assets/ica/ica-pre-v6.png diff --git a/docs/apps/interchain-accounts/legacy/auth-modules.md b/docs/apps/interchain-accounts/legacy/auth-modules.md new file mode 100644 index 00000000000..adb3119ad94 --- /dev/null +++ b/docs/apps/interchain-accounts/legacy/auth-modules.md @@ -0,0 +1,268 @@ + + +# Building an authentication module + +## Deprecation Notice + +**This document is deprecated and will be removed in future releases**. + +Authentication modules play the role of the `Base Application` as described in [ICS-30 IBC Middleware](https://github.com/cosmos/ibc/tree/master/spec/app/ics-030-middleware), and enable application developers to perform custom logic when working with the Interchain Accounts controller API. {synopsis} + +The controller submodule is used for account registration and packet sending. It executes only logic required of all controllers of interchain accounts. The type of authentication used to manage the interchain accounts remains unspecified. There may exist many different types of authentication which are desirable for different use cases. Thus the purpose of the authentication module is to wrap the controller submodule with custom authentication logic. + +In ibc-go, authentication modules are connected to the controller chain via a middleware stack. The controller submodule is implemented as [middleware](https://github.com/cosmos/ibc/tree/master/spec/app/ics-030-middleware) and the authentication module is connected to the controller submodule as the base application of the middleware stack. To implement an authentication module, the `IBCModule` interface must be fulfilled. By implementing the controller submodule as middleware, any amount of authentication modules can be created and connected to the controller submodule without writing redundant code. + +The authentication module must: + +- Authenticate interchain account owners. +- Track the associated interchain account address for an owner. +- Send packets on behalf of an owner (after authentication). + +> Please note that since ibc-go v6 the channel capability is claimed by the controller submodule and therefore it is not required for authentication modules to claim the capability in the `OnChanOpenInit` callback. When the authentication module sends packets on the channel created for the associated interchain account it can pass a `nil` capability to the legacy function `SendTx` of the controller keeper (see [section `SendTx`](#sendtx) below for mode information). + +## `IBCModule` implementation + +The following `IBCModule` callbacks must be implemented with appropriate custom logic: + +```go +// OnChanOpenInit implements the IBCModule interface +func (im IBCModule) OnChanOpenInit( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID string, + channelID string, + chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + version string, +) (string, error) { + // since ibc-go v6 the authentication module *must not* claim the channel capability on OnChanOpenInit + + // perform custom logic + + return version, nil +} + +// OnChanOpenAck implements the IBCModule interface +func (im IBCModule) OnChanOpenAck( + ctx sdk.Context, + portID, + channelID string, + counterpartyVersion string, +) error { + // perform custom logic + + return nil +} + +// OnChanCloseConfirm implements the IBCModule interface +func (im IBCModule) OnChanCloseConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + // perform custom logic + + return nil +} + +// OnAcknowledgementPacket implements the IBCModule interface +func (im IBCModule) OnAcknowledgementPacket( + ctx sdk.Context, + packet channeltypes.Packet, + acknowledgement []byte, + relayer sdk.AccAddress, +) error { + // perform custom logic + + return nil +} + +// OnTimeoutPacket implements the IBCModule interface. +func (im IBCModule) OnTimeoutPacket( + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, +) error { + // perform custom logic + + return nil +} +``` + +The following functions must be defined to fulfill the `IBCModule` interface, but they will never be called by the controller submodule so they may error or panic. That is because in Interchain Accounts, the channel handshake is always initiated on the controller chain and packets are always sent to the host chain and never to the controller chain. + +```go +// OnChanOpenTry implements the IBCModule interface +func (im IBCModule) OnChanOpenTry( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID, + channelID string, + chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + counterpartyVersion string, +) (string, error) { + panic("UNIMPLEMENTED") +} + +// OnChanOpenConfirm implements the IBCModule interface +func (im IBCModule) OnChanOpenConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + panic("UNIMPLEMENTED") +} + +// OnChanCloseInit implements the IBCModule interface +func (im IBCModule) OnChanCloseInit( + ctx sdk.Context, + portID, + channelID string, +) error { + panic("UNIMPLEMENTED") +} + +// OnRecvPacket implements the IBCModule interface. A successful acknowledgement +// is returned if the packet data is succesfully decoded and the receive application +// logic returns without error. +func (im IBCModule) OnRecvPacket( + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, +) ibcexported.Acknowledgement { + panic("UNIMPLEMENTED") +} +``` + +## `OnAcknowledgementPacket` + +Controller chains will be able to access the acknowledgement written into the host chain state once a relayer relays the acknowledgement. +The acknowledgement bytes contain either the response of the execution of the message(s) on the host chain or an error. They will be passed to the auth module via the `OnAcknowledgementPacket` callback. Auth modules are expected to know how to decode the acknowledgement. + +If the controller chain is connected to a host chain using the host module on ibc-go, it may interpret the acknowledgement bytes as follows: + +Begin by unmarshaling the acknowledgement into `sdk.TxMsgData`: + +```go +var ack channeltypes.Acknowledgement +if err := channeltypes.SubModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil { + return err +} + +txMsgData := &sdk.TxMsgData{} +if err := proto.Unmarshal(ack.GetResult(), txMsgData); err != nil { + return err +} +``` + +If the `txMsgData.Data` field is non nil, the host chain is using SDK version <= v0.45. +The auth module should interpret the `txMsgData.Data` as follows: + +```go +switch len(txMsgData.Data) { +case 0: + // see documentation below for SDK 0.46.x or greater +default: + for _, msgData := range txMsgData.Data { + if err := handler(msgData); err != nil { + return err + } + } +... +} +``` + +A handler will be needed to interpret what actions to perform based on the message type sent. +A router could be used, or more simply a switch statement. + +```go +func handler(msgData sdk.MsgData) error { +switch msgData.MsgType { +case sdk.MsgTypeURL(&banktypes.MsgSend{}): + msgResponse := &banktypes.MsgSendResponse{} + if err := proto.Unmarshal(msgData.Data, msgResponse}; err != nil { + return err + } + + handleBankSendMsg(msgResponse) + +case sdk.MsgTypeURL(&stakingtypes.MsgDelegate{}): + msgResponse := &stakingtypes.MsgDelegateResponse{} + if err := proto.Unmarshal(msgData.Data, msgResponse}; err != nil { + return err + } + + handleStakingDelegateMsg(msgResponse) + +case sdk.MsgTypeURL(&transfertypes.MsgTransfer{}): + msgResponse := &transfertypes.MsgTransferResponse{} + if err := proto.Unmarshal(msgData.Data, msgResponse}; err != nil { + return err + } + + handleIBCTransferMsg(msgResponse) + +default: + return +} +``` + +If the `txMsgData.Data` is empty, the host chain is using SDK version > v0.45. +The auth module should interpret the `txMsgData.Responses` as follows: + +```go +... +// switch statement from above +case 0: + for _, any := range txMsgData.MsgResponses { + if err := handleAny(any); err != nil { + return err + } + } +} +``` + +A handler will be needed to interpret what actions to perform based on the type URL of the Any. +A router could be used, or more simply a switch statement. +It may be possible to deduplicate logic between `handler` and `handleAny`. + +```go +func handleAny(any *codectypes.Any) error { +switch any.TypeURL { +case banktypes.MsgSend: + msgResponse, err := unpackBankMsgSendResponse(any) + if err != nil { + return err + } + + handleBankSendMsg(msgResponse) + +case stakingtypes.MsgDelegate: + msgResponse, err := unpackStakingDelegateResponse(any) + if err != nil { + return err + } + + handleStakingDelegateMsg(msgResponse) + + case transfertypes.MsgTransfer: + msgResponse, err := unpackIBCTransferMsgResponse(any) + if err != nil { + return err + } + + handleIBCTransferMsg(msgResponse) + +default: + return +} +``` + +## Integration into `app.go` file + +To integrate the authentication module into your chain, please follow the steps outlined in [`app.go` integration](./integration.md#example-integration). diff --git a/docs/apps/interchain-accounts/legacy/integration.md b/docs/apps/interchain-accounts/legacy/integration.md new file mode 100644 index 00000000000..4a2f99fd692 --- /dev/null +++ b/docs/apps/interchain-accounts/legacy/integration.md @@ -0,0 +1,195 @@ + + +# Integration + +## Deprecation Notice + +**This document is deprecated and will be removed in future releases**. + +Learn how to integrate Interchain Accounts host and controller functionality to your chain. The following document only applies for Cosmos SDK chains. {synopsis} + +The Interchain Accounts module contains two submodules. Each submodule has its own IBC application. The Interchain Accounts module should be registered as an `AppModule` in the same way all SDK modules are registered on a chain, but each submodule should create its own `IBCModule` as necessary. A route should be added to the IBC router for each submodule which will be used. + +Chains who wish to support ICS-27 may elect to act as a host chain, a controller chain or both. Disabling host or controller functionality may be done statically by excluding the host or controller module entirely from the `app.go` file or it may be done dynamically by taking advantage of the on-chain parameters which enable or disable the host or controller submodules. + +Interchain Account authentication modules are the base application of a middleware stack. The controller submodule is the middleware in this stack. + +![ICApreV6](../../../assets/ica/ica-pre-v6.png) + +> Please note that since ibc-go v6 the channel capability is claimed by the controller submodule and therefore it is not required for authentication modules to claim the capability in the `OnChanOpenInit` callback. Therefore the custom authentication module does not need a scoped keeper anymore. + +## Example integration + +```go +// app.go + +// Register the AppModule for the Interchain Accounts module and the authentication module +// Note: No `icaauth` exists, this must be substituted with an actual Interchain Accounts authentication module +ModuleBasics = module.NewBasicManager( + ... + ica.AppModuleBasic{}, + icaauth.AppModuleBasic{}, + ... +) + +... + +// Add module account permissions for the Interchain Accounts module +// Only necessary for host chain functionality +// Each Interchain Account created on the host chain is derived from the module account created +maccPerms = map[string][]string{ + ... + icatypes.ModuleName: nil, +} + +... + +// Add Interchain Accounts Keepers for each submodule used and the authentication module +// If a submodule is being statically disabled, the associated Keeper does not need to be added. +type App struct { + ... + + ICAControllerKeeper icacontrollerkeeper.Keeper + ICAHostKeeper icahostkeeper.Keeper + ICAAuthKeeper icaauthkeeper.Keeper + + ... +} + +... + +// Create store keys for each submodule Keeper and the authentication module +keys := sdk.NewKVStoreKeys( + ... + icacontrollertypes.StoreKey, + icahosttypes.StoreKey, + icaauthtypes.StoreKey, + ... +) + +... + +// Create the scoped keepers for each submodule keeper and authentication keeper +scopedICAControllerKeeper := app.CapabilityKeeper.ScopeToModule(icacontrollertypes.SubModuleName) +scopedICAHostKeeper := app.CapabilityKeeper.ScopeToModule(icahosttypes.SubModuleName) + +... + +// Create the Keeper for each submodule +app.ICAControllerKeeper = icacontrollerkeeper.NewKeeper( + appCodec, keys[icacontrollertypes.StoreKey], app.GetSubspace(icacontrollertypes.SubModuleName), + app.IBCKeeper.ChannelKeeper, // may be replaced with middleware such as ics29 fee + app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, + scopedICAControllerKeeper, app.MsgServiceRouter(), +) +app.ICAHostKeeper = icahostkeeper.NewKeeper( + appCodec, keys[icahosttypes.StoreKey], app.GetSubspace(icahosttypes.SubModuleName), + app.IBCKeeper.ChannelKeeper, // may be replaced with middleware such as ics29 fee + app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, + app.AccountKeeper, scopedICAHostKeeper, app.MsgServiceRouter(), +) + +// Create Interchain Accounts AppModule +icaModule := ica.NewAppModule(&app.ICAControllerKeeper, &app.ICAHostKeeper) + +// Create your Interchain Accounts authentication module +app.ICAAuthKeeper = icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], app.ICAControllerKeeper) + +// ICA auth AppModule +icaAuthModule := icaauth.NewAppModule(appCodec, app.ICAAuthKeeper) + +// ICA auth IBC Module +icaAuthIBCModule := icaauth.NewIBCModule(app.ICAAuthKeeper) + +// Create controller IBC application stack and host IBC module as desired +icaControllerStack := icacontroller.NewIBCMiddleware(icaAuthIBCModule, app.ICAControllerKeeper) +icaHostIBCModule := icahost.NewIBCModule(app.ICAHostKeeper) + +// Register host and authentication routes +ibcRouter. + AddRoute(icacontrollertypes.SubModuleName, icaControllerStack). + AddRoute(icahosttypes.SubModuleName, icaHostIBCModule). + AddRoute(icaauthtypes.ModuleName, icaControllerStack) // Note, the authentication module is routed to the top level of the middleware stack + +... + +// Register Interchain Accounts and authentication module AppModule's +app.moduleManager = module.NewManager( + ... + icaModule, + icaAuthModule, +) + +... + +// Add fee middleware to begin blocker logic +app.moduleManager.SetOrderBeginBlockers( + ... + icatypes.ModuleName, + ... +) + +// Add fee middleware to end blocker logic +app.moduleManager.SetOrderEndBlockers( + ... + icatypes.ModuleName, + ... +) + +// Add Interchain Accounts module InitGenesis logic +app.moduleManager.SetOrderInitGenesis( + ... + icatypes.ModuleName, + ... +) + +// initParamsKeeper init params keeper and its subspaces +func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey sdk.StoreKey) paramskeeper.Keeper { + ... + paramsKeeper.Subspace(icahosttypes.SubModuleName) + paramsKeeper.Subspace(icacontrollertypes.SubModuleName) + ... +``` + +## Using submodules exclusively + +As described above, the Interchain Accounts application module is structured to support the ability of exclusively enabling controller or host functionality. +This can be achieved by simply omitting either controller or host `Keeper` from the Interchain Accounts `NewAppModule` constructor function, and mounting only the desired submodule via the `IBCRouter`. +Alternatively, submodules can be enabled and disabled dynamically using [on-chain parameters](./parameters.md). + +The following snippets show basic examples of statically disabling submodules using `app.go`. + +### Disabling controller chain functionality + +```go +// Create Interchain Accounts AppModule omitting the controller keeper +icaModule := ica.NewAppModule(nil, &app.ICAHostKeeper) + +// Create host IBC Module +icaHostIBCModule := icahost.NewIBCModule(app.ICAHostKeeper) + +// Register host route +ibcRouter.AddRoute(icahosttypes.SubModuleName, icaHostIBCModule) +``` + +### Disabling host chain functionality + +```go +// Create Interchain Accounts AppModule omitting the host keeper +icaModule := ica.NewAppModule(&app.ICAControllerKeeper, nil) + +// Create your Interchain Accounts authentication module, setting up the Keeper, AppModule and IBCModule appropriately +app.ICAAuthKeeper = icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], app.ICAControllerKeeper) +icaAuthModule := icaauth.NewAppModule(appCodec, app.ICAAuthKeeper) +icaAuthIBCModule := icaauth.NewIBCModule(app.ICAAuthKeeper) + +// Create controller IBC application stack +icaControllerStack := icacontroller.NewIBCMiddleware(icaAuthIBCModule, app.ICAControllerKeeper) + +// Register controller and authentication routes +ibcRouter. + AddRoute(icacontrollertypes.SubModuleName, icaControllerStack). + AddRoute(icaauthtypes.ModuleName, icaControllerStack) // Note, the authentication module is routed to the top level of the middleware stack +``` \ No newline at end of file diff --git a/docs/apps/interchain-accounts/legacy/keeper-api.md b/docs/apps/interchain-accounts/legacy/keeper-api.md new file mode 100644 index 00000000000..d4e5d2cd9c2 --- /dev/null +++ b/docs/apps/interchain-accounts/legacy/keeper-api.md @@ -0,0 +1,121 @@ + + +# Keeper API + +## Deprecation Notice + +**This document is deprecated and will be removed in future releases**. + +The controller submodule keeper exposes two legacy functions that allow respectively for custom authentication modules to register interchain accounts and send packets to the interchain account. + +## `RegisterInterchainAccount` + +The authentication module can begin registering interchain accounts by calling `RegisterInterchainAccount`: + +```go +if err := keeper.icaControllerKeeper.RegisterInterchainAccount(ctx, connectionID, owner.String(), version); err != nil { + return err +} + +return nil +``` + +The `version` argument is used to support ICS-29 fee middleware for relayer incentivization of ICS-27 packets. Consumers of the `RegisterInterchainAccount` are expected to build the appropriate JSON encoded version string themselves and pass it accordingly. If an empty string is passed in the `version` argument, then the version will be initialized to a default value in the `OnChanOpenInit` callback of the controller's handler, so that channel handshake can proceed. + +The following code snippet illustrates how to construct an appropriate interchain accounts `Metadata` and encode it as a JSON bytestring: + +```go +icaMetadata := icatypes.Metadata{ + Version: icatypes.Version, + ControllerConnectionId: controllerConnectionID, + HostConnectionId: hostConnectionID, + Encoding: icatypes.EncodingProtobuf, + TxType: icatypes.TxTypeSDKMultiMsg, +} + +appVersion, err := icatypes.ModuleCdc.MarshalJSON(&icaMetadata) +if err != nil { + return err +} + +if err := keeper.icaControllerKeeper.RegisterInterchainAccount(ctx, controllerConnectionID, owner.String(), string(appVersion)); err != nil { + return err +} +``` + +Similarly, if the application stack is configured to route through ICS-29 fee middleware and a fee enabled channel is desired, construct the appropriate ICS-29 `Metadata` type: + +```go +icaMetadata := icatypes.Metadata{ + Version: icatypes.Version, + ControllerConnectionId: controllerConnectionID, + HostConnectionId: hostConnectionID, + Encoding: icatypes.EncodingProtobuf, + TxType: icatypes.TxTypeSDKMultiMsg, +} + +appVersion, err := icatypes.ModuleCdc.MarshalJSON(&icaMetadata) +if err != nil { + return err +} + +feeMetadata := feetypes.Metadata{ + AppVersion: string(appVersion), + FeeVersion: feetypes.Version, +} + +feeEnabledVersion, err := feetypes.ModuleCdc.MarshalJSON(&feeMetadata) +if err != nil { + return err +} + +if err := keeper.icaControllerKeeper.RegisterInterchainAccount(ctx, controllerConnectionID, owner.String(), string(feeEnabledVersion)); err != nil { + return err +} +``` + +## `SendTx` + +The authentication module can attempt to send a packet by calling `SendTx`: + +```go +// Authenticate owner +// perform custom logic + +// Construct controller portID based on interchain account owner address +portID, err := icatypes.NewControllerPortID(owner.String()) +if err != nil { + return err +} + +// Obtain data to be sent to the host chain. +// In this example, the owner of the interchain account would like to send a bank MsgSend to the host chain. +// The appropriate serialization function should be called. The host chain must be able to deserialize the transaction. +// If the host chain is using the ibc-go host module, `SerializeCosmosTx` should be used. +msg := &banktypes.MsgSend{FromAddress: fromAddr, ToAddress: toAddr, Amount: amt} +data, err := icatypes.SerializeCosmosTx(keeper.cdc, []proto.Message{msg}) +if err != nil { + return err +} + +// Construct packet data +packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: data, +} + +// Obtain timeout timestamp +// An appropriate timeout timestamp must be determined based on the usage of the interchain account. +// If the packet times out, the channel will be closed requiring a new channel to be created. +timeoutTimestamp := obtainTimeoutTimestamp() + +// Send the interchain accounts packet, returning the packet sequence +// A nil channel capability can be passed, since the controller submodule (and not the authentication module) +// claims the channel capability since ibc-go v6. +seq, err = keeper.icaControllerKeeper.SendTx(ctx, nil, portID, packetData, timeoutTimestamp) +``` + +The data within an `InterchainAccountPacketData` must be serialized using a format supported by the host chain. +If the host chain is using the ibc-go host chain submodule, `SerializeCosmosTx` should be used. If the `InterchainAccountPacketData.Data` is serialized using a format not supported by the host chain, the packet will not be successfully received. \ No newline at end of file diff --git a/docs/assets/ica/ica-pre-v6.png b/docs/assets/ica/ica-pre-v6.png new file mode 100644 index 0000000000000000000000000000000000000000..acd00d1294b3e1034b5d75b0969a42cc4b342917 GIT binary patch literal 34698 zcmc$G^;?x&)a|AeQ9`5yN$EyPIs^oyQ<0LEmhKV-q@+u_Hv%FcT~Y!9N=k#2bV*8l zbD!_If5Cn3bAM3J!Ts)c#awfaG3Ho?si{21#UaN*AP~3;@-i9-1p0OOMTd|={J0cWM_{{yvpt=Vc#ipWWbytcoq>r5Vh<@^y9iATBfiA2@5-#8kf(Iq>^A!v`^dk<`TNeqt;52xI_HIBJ@ z>N}1rHZ!U{I;ws6@2UjZa2UaZ8o)PfX4D^QSpOe?Lm=VhlCQ;+!9eIrZg^^}uD9 z<;K%8=^l>!RJKw5n}>$s$5#DMO57Im^o-}K-;?XEH~H*Hj1xJhvjFL5!KM0 zu+MQ;c(+&nDa0qrH{IPEA&?sG@|@8$P1ID^&?@Voh-KbmUGSy#Y}684avvS#(!8S) zN4GhwSS+nu_lHSLW7x_$T$IN%Nk3BH@aHlEW79VS`;Y`~r5GgD& zzAa*WYm7v1S|Gni(1{gw=W*|l8_}{+mEadLy4=5tf!3)3I~L%qsh zHM45VM02#V#G!hfo?=OS$kz5#VJ1158q4S9tAf$)xfy)R^&8_0XMVg!8w}0D(nYF> zP*v8d$myx}ecWX)^O>B<&lWTtlGqMrtCQ}D$hHVoXNGFiw&>kSG!5f-rU7H zvezjQ6_*;-Phz~XMy5U~@!Lw>Fo_@9D0%trmwbdqw3wRv`}OCpfBPqGPi7{2odjv= zhdv%}<@;GydVMa6FHzglIjio}ODia-Et_O4cnW6@9hk-b$F)91h?}AACJ@drjq<=c%;NeX1%66e5?G# zM(I}hTW)@xvOCQMD!zwYt$0O=1L3FV*Q#v}4{n)Vj8};lxFsV4KaNqAUuDb4GQ_OD zC!joZMXO4+@@%+nM7$g$lj@ogkLmEr!`n$=%C}hMcK#@4mY#qA=z#aa{%P!=Q?@_I z!x<;)O;;?Wpp$Ay-iCid@|LRaaZ=!MIJlBx?!#w8t z2JrK$aaBcoh*j6%Mxvpj7nGpP84JlxdF>{nkdjw--5;C~!7T3pJX zN_?QB-DUH-gY0lQ?Z#*6&+DBl<4h+bPc)zMBNJnpYWgZ)aI0k+l-m{!#OWd$n}fe> z6wZFUW-Q@Bthcv8d_7d5qalt}gcvPr^1^Sdjy!^Zwf@;Q)>LO@LgGpnffiO%h8#Z+ zA0&?`0hjuQR~tvSo7*z2-!@u*;{9EdiI2OdVzBW+XRbU$ zxvPril@n^^r6_^2%1QT~ch?)htLK7aE0P;SKhk?ow| zv;+ZClkVqCL|LnG)@=)4kGuh^A*@Q8psEMjOr(iVGU|w#>s1hlTAbK#$sG}LQVv

-X0lt-%*Y^>T_m~>6d~+yx&*Qo+&8Y7S|QQ1 zD*J;}iJ50-aS;ezZLHImmQq<#h6B`jRHHhwsxpB$ny&eM69Lw_@{NMy{zu3#K%JaYbS#TRf$+jHjTz8%pVK$@<^7 zt|Mu9PTPyxR0N+QX|9%yy1KCmVf@4jexD|$I|zQtQwJei&7}x$m@55f*9BlvLQ|j8 z$HF5g?P|{YVt*;@Tm}EX8DpKs4QO3t`Ti+JUv?f4M*R$PNaUtWp+i9axzxQyOPL?H z)2X-`;VUFLtn-g=DQLgX)XEYTTSb!Qagt2y)P9JaVeHAwzwy#Y${~O?Ku>fT-r9WfJN!C6)spvj ziv#MblWxN%FMf~N%foX2NNNeg1||`aGy+0WQun2AF_#zrC?e&g<;#?ml)f9M=o)%1 ze|>Y)dFA`*pWJ9gMa4vp=VN&aaZ^_&ZFW;)Z!>b+#)DNzGP+1m)6Yc zHF>$tHT(AU^(Ao{=&7s!ocG+AYq^?oXle2}-FKh$3?bNGic>Xg_L*3AGB-C@P7{cH z5_BHLbycJH#b(@o;p-dc)d7Phufw7IC;r5wH|^PO$qpb_*_A+8661#li3U(*zGP;kWV8 zim#KZ6iST4pI)@R^@sGNg@lAWJw5Fvt9VkB1ouqZu2_OZ0*KBFf6cQYa4iQuoURpSZa3^Shfw=H zE!PztXH-tTMI~Ab)92ygnQVRb?AaYAm5Au*!g#!U4|iwW=Q!BewW7=Ps@E&Jsh%h+ zGX&ogaxy#JUxvjxJ6Vio*eP^S%@92u(Nyj1=;-L~R!tL7_O_?d($eba>iS|gB_KKY zkc(?4-F^1-AoI$$;wijcJg&z@ne-;YsFB1>fo47S7tN=mxJ}Qv=*)+Dy@=iYQC*|k z(zE_oz6!DHH*~bMtBu>x!#kz1E1t5yzJ{x)q_i}}rdt+IU;OFQ?&ZH7!LzEX(Icx-%fStEl&{-Lo~u?Q@qZ@->h<`m)x?bmpUZIq!h2i zu)gc`5(4fb{H*2b@?VmX--LBF9;NW#ZfgJOdRH4cxvqh9k)68LjKU1B^?y4pg98I9 zs;boDJ}18_OqCh$ys4|J<9Pl> zE7C>9!^7jniv{nUCd+C)$TIC;zj7KiH^K>uq!47Jr^ghxT67T(4-bD{Wu>0Py6flv z=STMQFSZjErria~X%_vd39+%U>FF0g9^qf?w&MpHu_NBG%4-pZ(PK4y!94cFqU;R$ z6#%&-zS;L5=hIJFHsfXY+eDc^~zvqSk9kuKO_Y1vSaWnR-XpqTCIQ008TM@G?a}Z%WO~to^PKjHQ<;m{C!Rnw_7~!)m5T(?^>i{S=h(3S{s*A;R1U?t7ri^E(7ffFnhzs=Z3 zgNCs!w{rcu&l!aTwgPE0_72mv6>3vwbRminQY@)Fmtwwslb^ot zsC;kMeCE{DZ#c2QuU)UI;l9TqQ&DCAey`Sdwt(l-1_!OB&ua|f81-?rG-}9sf3p>k zI9-anef##I3PTBeEAZ-62kcp6f)SSQ?Kj|K^M&Igj>`j z((13%igDuAWJ#otl_$>wZ%x}0_&~{$yi0_2LfnI%-#g;K9f!NptChn$FD8#b5YT*I zWLG0M(-baJCFi|??~0C)%u1}w6=MT9GW)Pih%} zPQ=OxP}see(Vf;zTgrie?7sa8>U%%Y2k%pG)w0&!`|lg%Nyq>*wHXsJ({eJQU@kfV zmMfc1Jon$O9<8vaJn4RX$X=uJNmC70s=l@t*r6~C$o|jw=qAYB)k@b zK+td-1;%Fik3NK;DsXIjnS^AW=Y*x9;g%-D)i#nUER>5gE^*O~uX8K`s$tz6^q_*E z-l6fwD{TTc#hKbU1j3PDTkR5eLeTKf;fOI2YwxtV?uY;V`LiN(wmdDVLejt2vKu=L zEIP3XPg?Neq=W*_yev;mFP+40SZn{hzJuc`Q+Qt`-`}k?zeDN_frk zjBeJ>L0Z)8un7BS-u9DtEA-u94|#spuc5P&cVE~umCTlp&Q6S)%$!`$SCrQ~_^Y=` z@9?22D%d9^`KW!BT%=^|Qg`;5DxQ_y4NnOw?fcStkAtg8v0p62WE5DI5G~iq_=TQc zi%B_C;aj16tQNGFp7AwKuxN!@v-{ptgVp`liaOR3y3X-04g2L-vV$zs{8iMI(@#4G z&sF}ee41oneNlB+tulY9(Thy^?Xt#L$0i5I){x)an}&44>=7%X&>bS7spbzwulXaq zhL57(z2sD*z|T%?on^Nk9`DHYWfCyb&(o(Y(UFNKyu3~Rv)_s97m~F@zEb?#0nVAR z@S|86rG@%W*lknrBH^u|dZ2jUem+`Z;OwPw8PIwRkYyQ%h$ zkJxp~e0?hQlAb#-ZuA^P>f_y9dQa~;$x4j=o$ThknxxPRS3}a$2KfZeNv3;F(W|~| zxCd_YekmWXRyjI2j&;1mE{G?bee9m(GC$h&mGV1|PwU(HIBu_I&r@QN&RuT6Ts~8% zQux%um3U=x(SMlvo6Y;6H8wSMvm@3$Gq0Bu#H7R{=$dCH)jKQH7v*Jl6nD!1j^s?g zcQv{Mna#D{ml8;YN#Skw#Lu4=>Un&P+r5?*zA0vmHHp=()Fx&N z^EkG#eN_IYP_KWMD!#%R-*d>CBwz5Es%@;#_>z<6E#Z(yIq|7xbMd1#)t|`Mm;2tE z{7~;dQck|+(l@yM=T?wcPii1TexQee1nbU^6Dp-wcYWG?{E7QUR>wyCM%+n8Wd{B+ z+8eLAXzhO?FPs^cxh1!LV5HrWwD(rCpGW(?*37d}T7y=W%wIB;qgIwNMhr3@C1!-ldDvEu%3s>H2&$L<5 zh*yjYfBbhx&k)zKr_*Mad91o%piCqLP0k5J`?x?|V~HkMMv#9r{Y81Yo$-1w|Y2Nk2$UaTi{ZgzBPR6x{SQ9GGbTlf0gj4f&i*; zfI}6vER^&AIu-x#eygFJn3w=0&U_su$ar|H7Z>7DiT-hGP$wD>rh)-r^OWh={Z4!l zk_f1OMU9T7~P!;BM@HM}OH9^8P?wlZ!x1g5T2l0fj= zMc9Cov==krnmSgx3MmzpedUCVx=f({9Wtri!h!;~&0jp?;^Gn#Eua_l^z_K( z-e$m%Uv9o?;Kujv)ku!^J~ znQ)Tn^v4l7{3Mn-%gLIFO40aVP{*%{ba8sAHk%83UL9ymEU zZMumlUJEeJ8Q-X5ZPg86<>Q_fVrF9lPbZMp zzvb55-Oa?vDBv)A4%!P4AQ+asl9J2AJooEoczE0%YFL9dVsde3`7ctUsmfGa3-}GOG=>;+`EVO7*q>sY3Zj= z$rLe~r9Zu@({FI!DJv@aGg_jnq!cl{wsc=U_&vxRCnqPHo14?q`b0zsse_p(ii%{W z$#!p9`EJ3eFPD>y0A~LzHFMni_P-Rx!3miEfz)QJoQM!K zlq4p<*7XZiE}+^UVq=FuJ?KiFvDeM4jE;=71KjTBb~I6G$+Lka>8O@z@|qPM2nZ+T zq7rdq7ZR#--b$qDu;i0;|e#CEdnq*|aF^v>FL+yXQ zH&U!Qt^Q-p1&F}8Z{+$tT@w?N)q!+XQ2%~++#n)FNFfFV9ubpA0Qt>*9DNC6UYn?7 zs!@YrJQ+6`b?|io%5Tu%1}f9(=_xZ0B509K61C^R4k;i6&p?yPessNBcweswDGid| z##rgin>Pp3MY@@C&=9EbpbZ_h76Uq4qEia8M*+V^as043B(cjZrhm_B4~vzH0P|2~U<5}~!A z^P+M>LsQd$Qx`@@E@=NzEN^)2+#*c~@`c^R=Vdqq72?DJ&m;Sm-$g~?;p2BxaAo00 z{jODtrV^`n-`RGTXVN@~2f97yI&J{WPSkZh{w#t5F|T7>@f29?AE&x39JM#bH1B?c z>`1N%^E(SIIA9x1n7an>{k#Qe*v#V)*%`ywJDOib51v0F>qt@wUNlB&I#SJ%>V zfg{_nT^}w{w-RDlUNf~7L?a_7r^99?6&f9VwdLUN4bo>ElWGnbTCM9J7hy$~<{vk- zu?az72B~9xuEifV&_)}pEqpE;VjjX2)VLRzHv(vCZwApo9w|cCT1eCcHKx*1^>S!a zPdl)8j{k1YP{61-I?WzP5trfngm0yu4rg9!83pz>qaPby;6X0L zCF9Qe^obyP6hVM#6sUot4T(!sI6bNa-QT(`8V%!GRwx2HiWNI(-bFYYXBIKaZneGO&+8`B86HJ^s>i|da zxdm2Wd4ile;>UY~G(&0<5{t?SDo7W2P} z*x%KXL6GYclbMo91;NDd?#5#vYF@Lp%DjgEv#1neWyN_dkKkkXF+^*Ih+hb#LO7QhEG~K`lp-SsfLX=+R2;_vXAo1`RMhUcJkJS`p5 zs~0$3)P?=yOnTBF!OJ_m<^tMYUO~ap+Hki?MV4qbPEF#_(9pYgINycfJN}H7KL2;L zRbw~h2HGVgu#eD8ut!zlrm7hQF9~{`=(N(`3W)u}|Tp`bcq#*nF@y={hgTq`iDB8>l zMPE4{mye62tB8KP*2<9&N3}nd|KI6qrij~Sa$=&+umN|962B4Oyi*+%(18pwio16w zs%^$WyoslW;BF_2GefPR#^r8^#O72@Ip{8+r9hPIiI9_!bXgKr*>`|01*z0+XI9L( zH2}2GoLkvAZIZF{oVT97#cbs;Zw)~6xug*j6B861cOd97dr+s(DMc`CUt^u0{_qtY z?3|!r4eWz*DxcI{Y2u(foO7-*DGbLFC~ZP+n@?fcU}1JvIfLK45lUB)B9KwnQm%r7 zyRy1^wd#Lq2~wxJm)aYewvsI7*F%W;#0Q@HOCalYcXVw2s%YQo!HDZwauEgrNr(6W zFE2>UBA~^w%R~N~(w*nBk$H)5EI|qM`s(U2iHUA!e_w8~;=5w@EI97!tU1-Gqze;M zQ)kA|QTbAjY?iUa3gpOgI&It}*UojD0eM5f(Q#l^*w-4nH}YKkC|eO0dFwH$z6 z*X{dq#u;~!1X`PJ4O3fRCTvl#4i^#B3Q*}j9o-lEnbf4DLOhbSL$BPQRu588sdRV^ zn^weq`(sfN8Mm?IVppWP6)L3}8XD^9S#y&CE>a^$U|_w=l* zmjK8H;LO+T1*rRbdwY@O{L*1k#6j;SY@@=$7M%4_Z^kgvsdC-W*`m>~w=X+evuK#{ zJKCHG#JFyP8mH|@LNSOmL0yt5Pza&eB5#Vp3;%kF8NIH8T>_OGKKh#nVA!YIvN0O( z_~j9pDNGq2K|EiamI1R#H+P#ofZ1%bp)e_9+zq3_H>`ZgtS4fY_=wt3dV2cvZvXRc zI1kX2ZL|i(dX2)uqf!a;7w5iJXKZow>1zWCME{nAwYJEnGUdK5&8-- z!irhgeS5lCvk+h6d>4`hK)d)MKnZe>A0KbeOxX(L`T!8X$R~2>#>mPh6ZLTX5}{IP z8bK-Klm`bD+CE(eHfnF)2K!^;EF&a$J@AAn z-|}w~oY|=Bl8^T0U*i`P7G^$t^&8#`>tJWjT@2un?4+BRm6g?2fI<-?;MzhMQ-&3! z36;V)`W|EMznc?xY&0Mt7vlijAqmIga=f0E4d4f);X0qw&!g5HAlgh;vlXPmhvZO8 zySRAjj;2nI;3Hb>j>f-v(=z1|^^Xe0V?b^ktU%#IEOkf1OH6O}TNa8Zu9xlWuU#xaD0YIt(@%+5n zn)vSBupC4dS{?L|cHjwd-?P?NEsRBHQX(jRg_1lGDDv}v{ae6P+_e*u=hiEh@z}yr zkBjF|qt&E-+{nNU+}S(V`XWXU#>4856T}@zpdO1&8v!;8Mui9-x#W9_Cc)fc>VwoV zx4TZL22Tm^D-kj%5Z+*j2(#~DQA=mi5b~mXOve-EAC(!RE6_9*%Ve+;Id)J^pQ&xc z-R0bI>=DKCA~{bXhB(RS3!il{re+SEYhKZ2aR$!Mysy>;8e$H4iH)A7Bn&l@O>aSG zz6JsZW2&8y#?_wIK1tYkl$$1cb$1}#F5of{Zf_SL`6u2mGt3Zx@@r%&xm&@^{C8K zyXRr=36z(rLzZygt%jWK%dv2e$L+e5K1<)`j>P!Og=(C98bqZ3=ip|>8H zh)GgfuRLO>SoGZG?%@k3B6NI~Z!@9YEBQf_J>O*S6ZF5$`pX?8DAd-XF++8JxN((y zbTuZEn)5o*1jEO5^(Q{Zi?`fzn2hdr!2}B1-^k1ruXlPo*G=UFKQO!-R`7n;RK+eJ~-YQ_|fgRV}u z!l6qun?B2!r)5-Ep)OZ6Fg1{1wuF4?uJbmHeD8vvBKn>hL8wnX4YzWVdTIDxR~Nb^2|}IjBv0aM2$A8hhN;9R=lni@ zOMpCD@p&}AFnST&QJ$rz%a+u#`o&5n|09xH__GjgUKNr?I0s8LG$e`d0Zn&L{ByOY z>&GAH+{HyRM-yJ3er3MRvbt}i=rqD~YeZGLh>km7`;Gd7vX}tQ{n(E`6b}zZob&0p zxtwGZ-((~Ob}Z$-(>wShtB~{V#EE0ITKh|&S)hBUww!M*lOJm)`xK@qt%D-rTw*-7 zkK3)iRLzKrSmb%pt_5CMlh}OJ|m)6|RA`=sRqIez~YqwIJ*;tc! zMozl-uDak=kG7WDR1$ODVUSdov@0>DdLbGOGczBQK0zAR0hEY|x=&X!>?%JIVkzhl+f>iNyV zYwF}_H9b7C#-rDYmlT!Nhd({57E3jhe7W~7^-LI@A8~;FKGv=}hAlGLON2l3fTk}j zR*FF-zeV%Qs7iW;qGecz%wjcxw4e$z^HL*s9j*HAD|&7YBt2!2*)N;ktMj^f(i8d-ao zRu0ycidxwW9hM7Nv!dl3OoVq%0nw(bbt3Ge%CH}F{}aSpsg8h&d+AyZ@7%VIGF zj5X2Ga>ZG>$tGxsX}88^uhtW^ogJYj#mBg93Ov6IvL&%X8tA@~MW&$)O`UnsF12hk zIZw)Os%yVbHXC|;ro0SIn4$!3)4Z8yEQ*}z1Ubl7vYWhezf~|0lJm{d%q2nJI=FrB zPrv-nn8htKX^_3eCT|~yhJ*|qc#88DJEVj)ewCOkhC2uono$36A~(z48K2>_8K&qh zGxK80{r4zluK3rrG8|~vzaALLQ>XafAI8xREg-9e*f{lgr8SA^SMYd^|LZ*k4GP@U zs?F^b%Pz*T?=k<{2;~6t58H0xFZv{LGu;Bk90k%q5ttI%DbBv8ZFTbjtBjUZ>@*|B ze?6tY#B}%C((Uk$$mwIkUi{a1x@EJx{23{IYBzPJc1Wzx_+0?zXIvGz_VLc&?8XP$ zCb8=H+CZ!VcIDBX!`XwirJN}KzNj6Bev}%u@?p*&r|`1(lv0EYJoiuqN|k90*`)baVglVJB6$?2DNK5sG()9VuBC+=f*8U=a9|Hemq@ z8mz%NthFVWQdyX&F<=I({0RF_SrtMYBtFF+O|yWSQY+G!pmScC71MxNMuZ`oyL|WJ zc#iAY=Z=L|6IRExu^u2p;IS|>Gs`mb+rl*>_)F+~-A;q{giZ62877OE*hM&3^S8df zM@O)X*6YjXkXv^ieF5bj=nCS~@zxshLW56HW)W1{sfujF{lRc6%0VwbPuP9|iH;)E(Wy><2fQ)Q9h$Tc)UGZs z8eG<#MC5nQ4mUVRo`BFpdn1?tP&asWpdC$0N(!v-=!^Ai<8IK5y+2@BkRcyB z%!=mjoiSZ=(W3s`9Mor5_&|_BK%Iz`%TUz&XtTFhvAQQeKi{BapOlz5UBsOecvRV) z(Mji{&1bHbfXhHV{l+%{0ss(X@USSQ3s-}!WAYJ@1F*6D1d0T`@Tn5ra!_G_Vo87} z?`!Z4Vq#*ym`(T*5U))C%Tox2r$Dw<-wCAIU7Q`lB3gr{O-^WAKT94m$zPf^j4N^ba5IQBZgT51z$a z*4NVmAJM;-@}{H7(m&DGV?EH{C%g4_s^zTOenvR?t$YBkM5}b!&A1*u`{`PgdB&*u z96UaPOKgDWu%8*Vsf0P6_J-kcY}f7mg2@Wl{@PuPme5eN{GKeuA_l76d)h}}cBz^3 z^FE$&zatH@>-jI!XxzIG%s?Apj4LjSlAEd=(eoY}9G!oVVY2f;kI9ea= z+hJFT1$rwEjs`GLm|0prXHNvbknp`In4`XH(Sp&{*?8rkxJ|X*X$BBllt~a}^LR(h zro~%nH}!?9>2K^&{Rb6G(AePoxc!U&@@Qq{?75%o=bW4z@c%KAzo^094uui%iHiQ> z>Tk4eZ?UE|N~o=^eN659x41Cl47djfvf0CHWrCH2xWCDsIQnA!RA6~Inm4Q*Rtnz% zv?BQZ;l{X8tz#fQQDRb(rKM$IetvPmGZmEqaLYmTYIIq1je$GmwzP? zbaQ>A>e_tUYZzgJ=fMhU*WGn+>$zXkit_nl)%+nSQZX6ocS6+NXaSQ7_y;>WJ1Hgn z^j+_tbY)4s2LIANxDGGDcM4uJ5LJR5tw`dIDz3`@s=ZVrSq%+8Qr6f|u~S`lu9E0)2-S2@eaS4F>fI#j8Q$U%`{= ze>DXK0ba$#z%T=9tXqTF$ykvhY(G$aP)tCv^@7w3B)u@xFO~Z9Q+CjSnpQKs2?+`9 zxf4=d_fr*Xr9SDEf};+iqBiEmF~tHB_X_#ON85cNJA^9rfq5?weiYB!xF?mPMMEoG zKA-p@ejVFXun$eErp95;59lwra>s@79)bZDEJd|c5GXF-Cxw2?VKkHVlpQPrcxsjF z-h&LfQ%}BN_7vY;YI4J3_%m375 zEFvNT@{{<<{A-^^I$ByZw_Y!#+6+qaKc{K4<*X~4@W2waL z4{VIGqT(6Y_CWM?N=Hg!vi_0QB9=*#WNEmIVV1*ux!=bX{2tzw+P?x>zwBwnZ}4d1 z=N+L8nvjuGB>XQyVH|zI;QBt0#|<>}<5_P9@bys$*s6)VfxoH&171e*lP6C|*`NM^ z<%F6C>r#Aw6NK~iiOSWI@@737O%`_c51?5(n_DTsT}M1i+$;IxN)1>y4|wF3&T z-`QFr2<&pbvI;DDtP@3`!3J9ekb$kZ@ zDL^9M^Sy3;O;BBXythtHJg7GHh{IK{&KL0gL*KoF6EX}fq1>{OBBr%7+*I&lA7^-P zuZ@?_K|}L+Yf9J9&<}()e!aB*FYY^rhK$u+YWn&NmKR&e&T<)ajR9on~IC|KqQmIEUA% zr1FIk?1KUJRi*iIZ^9)1H8!1($$(_2sTOVnQdIu%a1WB$a{I;yI7z$JZ=pL19k}I; zMX-93nL<10x9Ky;tA4r*PIUlh9%HA7`+7l@hPQi`0p1dX>JM&Y|Dy@ZSo-1x28C2H zZvk4f>DpJK0*3b&fB!yV7>|qedQZO=DL6|L{5@Tyri>d(2Fl2|7%2$mRktf$BVSo; zPl%rC(~mdRiHXX|JyFa;L@p!nib3NIzOfKwR&y@Nm+phT;hDFy{$z=J1yi>Dc|PbK1- zEq)gsl#sMtKr$|Mbpn)EQBeU0_y@_&@^g#II%otm#t35~yy?ykYoZGYBJCWhxi={Mh%i{gp zL9*wcjL~S9@{g( z4(m-JXwS~f{1D<5N)oj1Mko?9GBRNN$;l^>qlk%!P%eLH4ePr?R?gRnf6@N-rHv|d zXF>bqAi24-2o{Sfs~A>#ePSwTEDsH7gtWh?){|9VVuGYQWGfI&A;<~F14s!_QvjPR zDF+>YoyOHDug}X{I&Qi0g`Od7@%dRB_;k-I)g_>H&=?{Hy^c~efKsy9&zgPe<}Sg| zYm=r!BOmJmDFcA}UcnSd&anO|$KM)vc+>i!Vy{vFa8Av{d+ALg{SEvbbxZ%+8q(nO z*3WDsc)QZXXw4u&bar-HSL|M*GA^`-SGWA3bc+7m+_atLRY`g9CYs1~ZO9xV6Rr+G zj~n{gCC@J5a3Qsc!In=%K_LQJDkq0->k=#wC_Aptwcl{_gq4lWGy>QH3}X-`O(izT zx~R(p_e9w2SYzqIKj6@O`gX1Z z{$>l>Rt4s}gZ`J5D`JqC*M|!SVZFHw8^1YdDl@8lc&ew@+vn5yju`rM+^9s2mJ5^UhxU|sFR z;oBiXRS5J9&flgqD24{?<#Io%9j1+KCMYX`KIW<%{1nGMbRv&f&&9X-hI6P)!hg)?z(1(&YidY8q(hdQsrbtu56 zqY8%F=Q?5>&pzihHZ}s3@@l#SxB^fOa-9{p0djM4))j@J+nS+Zthn|FU#tp+9~Qf* z#k{Hj9%5X(^{dgdQsAZEBX6KwO^+elz^VSV<996LI@xYgp#*o%#KfA4imYIqkftDT zf#zHoZ8Lm;yE{Fx45&_wsZM#zU@ z#xmE3Njms^@r2NXT&Sd|NdCYqRHe`cTpJ)+@I%Rj>Iw{%kB8?Ks{GW|`KYD2n*qFm z+4jTH94noI?e26qg+))~G7^VF2yzD8Q~+@XDBFzZYC1s675+uI9Q3TwV-NWE*Y@_| z;bFMYby}yVk|A0LX%4nIS*bwcVoenpE)19%;G8P%80-i%-|QzoztLUx-mbIkjc0*N zYd1;Q<>lp{R+z{-Iqlz%7A3rUR{^`HukRn+Y&!|dg!@CV+jU93nT3ll%D5<8$1s{M z;a*GwT-u9ecoHGE2nF}tBH^@Aw*N3479J*I0OcDBqxQ|RG1-T)^w36^Yw(%16@d2e z^VmETOzHXd&11LiI!GX^^<6?2R66r!78ZEqe1G5|Ew9wMnLm+}lLLGTy)%q*UEsIi zf1(oisrB0OYRdR`ofYT|1bX2S>#b`T4hN8f8^`5UR4zA+u2AO)NJ(&TFeKEeEw@>Y zef1b{RJl??`h5h%{U;=cb$>r`I3y5PlM2|1EG3;M!INJQ7zX&6n0+AVJ{`S^gxCC|#+!6V;=cdl{J17RUzwZ>@3I321 zhV#=we?RzE&lA`ZrBuF`5E!u8K72c<+QslbE$tt~-)jOm_~+)Jc->)Cs)Z(^u)#PO zUs@&(cXxLo%7{rw5)%?ehj+u@y*r^C?C;m+;O5Nt8P$g%w-G;jrOXkP#C;r4ZiB0# zX2egaWh7;0A2?l9{yrr|2-=d+Y`!`xzgizr@cf$%K?H@{2TntKdpk55d3kw9R|jDJ zfPjE9ks9*Db;lX|8hQm3k_I42MM)_JHUDSsWAHA*9iB7^op!MfU<4@b1>AQaqznO) z$OMlCd>T11ube(b4*3o;A(Xvgpa;OIKLOf?M=rq{v^XQ^&;#nvGL+QQI@A@iq5M8z z=uFC~BQWV^UXVy3xMS$oV838JAo{xDN|i_chX>~GjyipEn8!X~G+GOkDVmurq3n@x zkqadhLeiXkDHzfYHUnt+rX?igH=BoE2R~9KuVqxH3F`I@Bpe&xjJl5>Z6WlbN9(sU4QK*TNQzbpAEaCI#&rm+Qtg8nyB_x=bLZY$V6Dssd;cAs1kl2&y z2TX#3$ABxrRX9F^2ez!#4Mz@(U(?MoEo0FofN^{LU>q=AWDRjvkZ1WQRfVweFfM372u!i@shw4AUk|jT?3=4oZMUCA_E)Ts;BHv64}+mb97E* z%G^}A;8rapMIXbzF^{QegQtNL>pOXP5=w*gQ64JzA~)+j zxPi={)=v^{{<|X#a*;P&3_C$g!tu*UOY6Jdb}I64RP*HC3araH>n?>uBkemAFE1}} z{(_uo0D?@048)SKt~2Ju-GZ$bFvGQ74Od~jk9L;p$fH(7@zf; z)GAfD{@{+OK-y}5T45PEn2f;_wOp*LrR8`7F3O^?uk%cKm+>! znw>JZt^hvzylsJ^4KmAeq#)>)urWRsHmG}tD*;UXtFj&kOT!eEX4@c28a~%4H#AUJ z2bs`FM+aon?sS9j&aadmu;6RMg&?KjQV4vvt~Ru3U%lgpA|Qtc3A%>GLKM_A{?8^3 zwsx-A_$<@rE*G-bj1Etie;-kV(X+8_0KQC<@b@#2{Z6Vy&%^}AkD&1kl-va-9lVf5 zIF*g{$^9!RB~F*Cni{U!J&vLhLkhnJZXoVBA0T|_0t*g`jJcT^IBNh8MiP7__1MJ| zV_=Y_NJ|U~3WEF3wJ=KnG>gQqzyt|rPJQ)*=hm}2Cv!zbFbh!$IW55rS%*U_Ah?hM z`z;Ot?6|-PPy>PG0DF413Mo)$8`c-%ZQ{1SJ^?_Xwsshp+5(d?{=J9B)p{2NBnJQI zQY4luLl+#bdhn`=_kS~T;c;G3Dt!epH%@Bk(N@m9PA4en4*AZf)*oO5EWb8yt8Oy* zfu5za!u???ZsUNtvrI_>*;f!r`gPjiJ~8sy>jdhR)K?9F2onE+E&P)Jm&g8B$8h9F zY(yRB5t&z7buMc|04%F)0zF8})03YyxN(dx0@u@H8e~wI1WoUbs%V>PM0hwVJb^V7 zx^?)u#+Ju`i1zy;T{v7ElA2z4eMVk+y`wRh=c>tZ;gCbrH=;FzbLG_os8i=)#G1Aj zr1oC0DF+Te>#>OIx{%pvk|OH4565W!HJ<)^`1-xT*<>~x}YFQYhG3c*3wkz_x zK~{zI`ps?@T#7(yqTHzuKW^Rp0ER`XO6GUqQmfWu5agkNju(Jw@G&hx3I}Dwb?PCm zJSZ&iZx@tvt>!SVEG_v!R|O@Ba2q3zuffT+SL~{~q~7t;@?6A!GwqSSR5}m!5!Auo z{DE9$i+v@~!TDa!bN~M9<29|^JyfB;Y)bTFzPno9 z$JW&I^kpV8wP z*WA}`2Jsd1P49L}9j-F_wa-Ef{r{)6_YTLpkKczcM1?Z4QwU{;6iS8c5OLX*P_k1( zCA&iQ&d7-Dy=S7VN|Kb72t{Nhr1+iJ{r&y({Pi5i^L&rvz8$4&e8&6p8t3ag&-aGo zkOE)Dn9~~IBt1PnG_JUEfN4LVxJDAATJF!QRzLP^P91O`ZmTzdNr3T2A}pJluB19` zVVAD$n(xA3-lo<8(B%1&OM zJcw9>N{2Q=d?9MwSf>{8$V;Oud&>Qcq%E~?nHO=dPr)ReoOuT??SB$WaZ9RlUDk%5 zCfjds3)0nqNdn>+D80 z#p!lO9(mUBc-wYIk`@%Bm+8|7@8t>U+#k9}dMnvDONxqDLMxFuIFCC)AiaoNI~%@X z#rq;_TWLvT1o#4Fjwiee#@yK`K^_G&li&L)~DmJX6!U`GLLL=8qF0Y*SZoalAiZD|8d&m zdNsX2T<$*+VtdZ7{+wjWvh6{iqx4McbJ|b`p6|i|><5OUXn6mV5j%CJvuc(J#oW&* zP7854tNou;mRqd4HBCBjZ|0989sNKsGE8q!E+57ESeP}S>~(q@7|4F6-|($!o$}uQ zY;ug4IjOviYFap@h&yHm{BT1t7^{^(R@_MLl1|Q>rMaQXiLC;Oe-aYc8yb_CdT6(J_n)zeI)eq>08EPwU ze)LZkw~Sfb71VJde(G^(wA7dNdyE#FopK5Z$=-@N$@VYT?()0xYfs%A`sEj-yBT=C z$7V59(o{j;NblTC+ijL`{jm1>xlh9M2Lu%w?lCtLb|UhS3$i8_0T?XS~gzU>h`3vmHUN%UDe?wlstu88Woc5~USWWC(# zRWX>W*X$~?+qk-tGveyx`I7H-ZA?zBKDoVBTmm2czD0}O>H1>oCUeVNcJql#_l2_B zL(&Fg|BPo>#%zl|mu)BX$hI+Q^zRxoch=q0>^yg$ZUpLvzaRcN#7m?xJMx4t=y=NL zZH4!@nVVi2o(SyUA5?aK-Fd^DrtjWrvp}}fP%`tEmnwrIrH?w*Hx0X_lbw=uxr!Q% z6gGuR?OuFyr?g9Te6zH}N?){E{(5)ip?ucy`WcSE2`%04OdcLBdu2~YGF)DvOHgKn(d`#8B z1%f{P`^Tw{*$(we?Xg|mu1Wgy0o6AJn0=St=tit|=rT^7ELvCh@?1!MvTfCYKDGl$1VQSqvG$O@K2`cG34n1 z@%LOa{zhLY2s>|bK62oRuCPUl*Fe7GyoPI6SX5ra%P*py<%{+LuZI5h4u%;xUu?RW zpB-)Jnr-uIpWmv;=>ETtj4S8QGzs1n?JSdVGWD3(;+#}U*7UinHg_jF zSnO36wn;3c!JwMp(mUao3YuJjW_t^@CBdi87*9@92F)q3e6^SLBV~#2;K;n|NnoiZT(20G1BiJ-C7+zLd#8C z{k15JmHq+!Puu-_`WobnFKx-GtHc>Mwl7sCrCcx2J9n8i-zs&WXnHOqw_Q6`|5onT zuR#K2%sLS@JA%`=TWu~{U!1@FbE{S4qtn!stz_ofKeyZ`%Oa@+a; zN#OOFWf0VaJHR#Zq}L{e1_vV7*4JA&9w5y~AFr&eB-vshEn8CKU1KWuZHyZ&oTWHm z7*jw7PS)@@DJd!NETm7L=E@`B7ldHTTXLx?ncrt-{Ek8d38L`C+}s?%n7O%K_A=!5 z5mXV9G`_yRLa}zJansY&OLh|WD_!!nF%Fx!xGTr6UVF(%zqmdm5Q=*qUOOk8%xsH=?ca6_xDR_x!=4wbK`iNO?Ow< zMSRx|tv$HTpk%IA+~JUuduw013>rKp^=rFi>4l@l1x`@#?ejn8-5@9m?u=@87@w_>ql~ z5xU9)yu1KXZ6uAaT$u)JX~{{DV-tQWwF_Du4nVbV1~c0|OJkPT^m$_j6()|ThOS>PkB^5ukX9KDM<`S)=X3RU?b@}nvLenv zcmOK9G`&VvS#7N+dIrvRAj6=#q)wy3-pk55(A#@$ZNV~O2jN^UxiKxdo;>rg#+fr` z93AKIjb{`U8yXwc-*RC3aP{32OavgYxGKeAw385?5@ISmIU=_9wRo~ zVdyd?6JHGto#5t%=Zpi81lm?0t5ppRocG9|QEY};+CF+&{+e`E1_w3v>p}g3tG0W` zjxm|AUpi?2jsP*3nwo-8h>e;M-bQV0K}LG{s_umg7Yq$q85q7yOyF7zcT8Ih6>UOh#?(IzEWD)x*=1|KLFlK1O^|!JV|Lx4>S*k)j*9 z1K$%>)$JlwJ|`kOt(%&FAh4I>E42VWspRTM#Y;l}7+G*Cs?+-J-Me22fr<=!_Vl4& z8yWF}rq_s;Q2pmb!y|&^fuD*_B(an)34Ua6^)p_fg4Q z2k$R%4iBh&#!a~)TB6II>7K{Vwzdl^D_K|nz#)dED%$z|JAPoXrTBdpfS(U8^{g|?QK z7Lgbm9)8Zt%S&HBM!eDCgdbEw;6~sq4Dwf2SAjlU zGBalxrrxzHG&IzP?PYtrYGyCz@f$lx@5|$kRC{oYkn>#r8q}fL z+1Yrg!otGP3ch`-GhIsFDh|~P?e5(lW>=^K51c-I;b~vXLqd%z^WVRJ(WhUe#yfjQ zM$C!C7t42yj1n}m9*JA}5t%DOneml0G+0|dzkN%<@3fz`G7<^H{z?ZV*l2OR#VPeI z(`4Lc^ub$%6g8ewn8+Pof_q_{!py;O9B?FBq6-&3FD$q;C^HjQ$A11iedC)>0aSXl zObBDZ7ISme({(`Jx@TGv%&(o_ zmqs#CL{L!uK%@XuFr+=EL~i@BA9r_)^YMMH8ge?q5sFDR*7LZm9pTxtTyhr3da3Bg zKkcIOZ*KO!aibzUJiMRGdM(QGN1HWm>$dINX$E{H$m9zML^fL5QhD7N$m>QZIA0)7 zv{4UmCm6CV2z8W+=#%-_KUKfw=?Pg{0-gzWk5pEuKnXQ0`o0q~a?;eq1wPJpQriwu)2vlV!v3QI~RQ#EkS|5_kZf z9nGSqZH-`!X+bRRQSYrk@+WLLNC*QBfF@t z^{=7c6!Pe3itFDE&hs$KBX>79Sx`grs@!f0T2%knj9+i8Y=A;53E+p?&tOo=_};U6{UtP;8Xpu<4v#r8oei zy~Fo(0?AYlMoZB4_4iZi5yamj{vHHT2naaPsml7_G1*t&@l~1>dfYZNG(@(+*qnk+ z4#}Uw;S!#Ff(sp>Z9u}14(kJ<9-)8|wYv;4aXb_RqK(y`bdViSyQWjm%NzYhQ+%dI z97BRfj~)T#9~Arbc6PnEw$l0tp+Md6C11 zK{E^Jw}2f&Nr`xV{MDq_Q&m+}V`F3B7PR>21o|Si9TFF(4;LYfX=BX!um1BtsL5^s z?ltJ%>d=djjULqh}c{| zp#`}MNE61!i3tfi`tWkjMvHSzdy58UEq4)yUnWRck@&kGKPE&+Tjf)a8>M5oR#YT* z{P?XFKgyE5NJ6FL@CtP~guDpWpS`m)Vj#2kfp|+cNk2ZhVjWPbQ&LW1vJbJ=$6a~i zV+(SJBh{Xq7ff}ediEMFk9Hplj1e)$U3}$=pOuMt76yKDIE71XY6^XG-$jWWza(z1 zU+q{&^K`6EwUaEBCSJe3>GDW&JD-hJK!vvSp zB$}GGcAOgD|4hwTs;+aYUQRNO6Fq$Ru!sofiJK1k`b;8JM3?v=xcd3r2fe%vQ`GvY zsmotvr|0MA5pK#qy;}YY@C*b$1Z0eksZAA_hXbmstIfowJ@@Akd^Uo@!nD=YKtofq zu-Kodwu6?I^O!?|zL1SE?%r>NB^ zFcTJ-P?sQ0zkZG4ysDt!VU8l+f!uNaJmU`vqKoj?U1pr^Up`$X$SLdSyhVxT@9&RD z2=KYf7!#~!kmoEpO~w7BO4w+rGJ6H0J4vtzog!5vvZlSgy}zFyEmbSt2VOQ=H@Cq8 z0Wtl2E)$}ehXJ$VnKS)%Zf0h~xT6qg;#s$rWEY_zE(8RzQf zJhZ@8@$Mm~deMUisg5Ch7DOxdNqyiZ8#9Iz365lFXy`v+oy&`f3jd6==~!++*?|%e zYh!z4_oOxbRd5K%$jHPW@QN2ml%SOf3)y-0O6QAgi}&lds0^ zJr{!AUBo^quZ%nzY5kFU$;)PGjyPl%`B(g5x$x>_qI;^+mhkHSsj<7}T@C&l{OTnf zyOebqYln6FXACuWk9oJ&-aI_{O=)&`aD%C+rsgn>YS5D5t)kle{Pulo8>^^8WG()V z7}hj8R{a!T{CW5B#$@2k%RVpVt8R6b#s%8x0#)_(PiJSQuN*pk?5WJF@X33gNg+e8 z!~X?5pkPSppCI!{y_}GGxm&uho>=%+~iGR94o>^X+b5%dDXasWz&W6W;#5+0)3eI{&1f+DDBLBeh#w(n+4d zml_&=)RZ*!QN}CKBr*i;>c8N;)K4mWR>oUHC%^~y_%RP$I= zxa9u1nY%#p2cPob2VrCLPpRS`-Aq619$509`|)G3oQ&+vzZ9dmzvB^=>u-7f1E77&fiXoO51)(^_+;{=*J6+ZLHrv zT{_k`I?A&=O&1ipFF8+Mf)>5<5m)Q6hwNnwF6n>8IAUVrxC^~roC(ngoG)Ik(DG`` zDa{Z%5OcEU+W5=MMB^9VkGn87eAIh8o|@?>%CwV0^cT4U@vXwKg5$=Gw~eZ(aG5sfhH>(vB3kPc_!(=l{pXS&|=HI(Aq%hwoDac?-+>;{A zf9sjd-rrC5Rk(~!X&d{ex7(w5j1fI2D|l<8za`AB=k0R0NQ|-*qYoYG}D^-8q>ZtAmhF7{qJ*W`Z zR{iP$f3 z9Ix{u9(s`B5z>=|Grf^w-CEUsx!K)Y_UVb`QwR6#d}?+r?H6;cxuMwjSEV-@)DLsq zdc~gFNbmep7FK=et~S+8vD-!k%~TiWOFl9uci2^)G+tS*AC{093Rn~7IC5D?@5_K{f~OX}RZN6zI#f?kJr??vMT_J2Iorxkn{07TJ*z_Vn5`8hEf*R5 z*H861Ov#4DX^b3c8&8zBF#FWr2SZA#fH)0yF0Sg6zh{FiITzhEkPl?;PuNPI`aR1| zOiR9dVlO_>h*_;o+?MEjUkHDh&3IOJa(AD(@C|y>z~-pd&m$K8M$EP(tgU7=@*uC; z{8Hk64uu1-BEZ=M_uQB`Vv%52!;o88XpXrzCX$xagd%^caf`>D9-3=u)Tx=dxg`Fx zfRK=%AfaYAhgxwr=leYQFN;(5K2WHA=5~kc1lW8NaIHPH*tW-{kVV-$a}nHo?WALc zWo541932>ap^c7*^OVsS>EX2c%i3TLa?;b&b8*3qxMypEYfAub+PV74{BAB27mp=X zJA5jGnKxWjK>N7%z;yKZ&Z2#ACcAp|JDP7hJ3FfJKfvBlOOgC#2`^c!#cqnXQ#V%2 zjy*P7SX>MW4koe2{;$tDT2W9{r33_Qf%RcD9H{;Dj6b`r`(Mn1VU6LL`uZj5V+QSu zp_n@fSJt$pQSU0t1T{pL^atqsu8fUAMH z{T=-f#X$7Uz58!lwCt*rhJX+LEht8zbHcElM2ZBVj81Mr9-}~LxDIM2kaU-y!Ijq) zV4}G7kHlWV(Aayu`vS`Rr}L%t^&|la2KRU8Yo}3nj6C)Q&;`m1X|^oVy*tcq8HZPF zQfUVQ#=zNoLxY2AyBdah^jP=qXa$+XYZXJ|EvUx!$+%o3j7)JqHguU_OKiDlYi9?< z35XHtgP>6G#~(oqjL*m+i6)Yqn`+5coS_SM}c~Kt0fefyPkVSH;=>59%HWslafRz447+S-07U`!q{ll_7`tKJ?^`;j&t0F`-sUjED(f5r42bac(+ zp}7SGe?b~_y?!0Acbu3xNlOS^&g{Z-abQJ~LdQq^h-62lq>`b+m64H=kmy~}smsiq z0BE=cE^Z)*N`K*;p9FkXw0fA4fq}=Y>sYVvH)^x8zV>ToK<&`fLxk+$AaXr1JpAU3 z8_-CCCbl2?x#9s52O76T1~LOs_}kPE9XeDD1}OS9K%h9*fsuA)sCJtjK1nXd0px}S zHq5k126Y=7(Nv%#-rW4_*=XUtEAKgweKh4>l)Kv8va#<2tXxPc3(%QryY^|Nx=*vc z3thV@m)X09r|pWhHE?4qTzvo?%s1WK-JOT3M8Wq%xVsC@s-3-k;hVYn`AtL%s9k=e z32_{*Sp*xf_dn*+JaGhJzfGm<4BuQGDnn19BgYp5{?;YrPgn4DkqRhk|%U*}6 zUl&#?2t)NCn0tF~K@f@k8E8Q}3aW#I7IY`DA>T=zbp6_KfB!83X=%fb>p<5L#XByJ zA#m=ar(c*pz4ghi?m0`MCJPJ8hrz)aTN^c?s9<8jB_2o&<|*K#>RQ&2>=Chb##t+? z%uE%S3jP807R)->MUq5pcqAO0olm>XF!d~B67cARyOW8@AZhCjut$t&uwy~;F-H>@ zml7jJHHi`Lt$zS=_2lKB;X_FjP6W|>i|ND0r3gmg$Ad!p!^90~0kjgdY#+gE2m4um z+#i?B0~|^v#twmo+XnxlY|_w?Ke>7ws8O-^KQx*7Ybv-w2#g~jbR#Ot=P0@lUu(w# z=-@o3pg<;=+pS^vw9gU142YjnT5#uE#g(Y6Q&duF1L-z6Bm^K#QCas_nbA;Ty`UYn z%>C}4iE_VmmJKh|9;yXyfCNf&N?1fhvt||2M53V~jjDm6VNa5Dc)>|%m*#QNu&hB) zePKA%;CVz=8hrc{Crk?shO{c6dcc&yy5=HuHeVmLhjY2}w*usu9Vlj%`vHwBxUTL$ zKDHR!-FtTK^gruLl}jX97w`H;#`$TZ?WQ*R$ZJG5;-GIvn!ORtCHN`yM>`jUKLjBknMb-5L zkPJL#Pdf0mh%uh2(utZWDJh)o-&g(sXuJh}8X~$}&-Bz3v{f)NR8N*ZRCC_?YnTI# zJvhoBx)v1|vsN)NF?CG33U@Ea75CebtR8WPqBX(&hi_63RR1+KA7eG`q&Qq#0R2I% zf?X#fDgwbh!bKxrmiWd<8cu45Jn5zB{m$o_L9JA337gHUsW4YGa9>2}kd%=F4xpL&q3q$> z@;jf(s+^myh4=gl5W2JBIC+Nd=+^S%c|%HJ@CZlx`}u!QV0;D8@{s5qh>e+)Ni=%D zBa@ymVLdG?ns5MFj zZIWK3lnq`6$|@?Lh?(W;Q+JV+Bbczs7T>)|^i6nVl=Mp*q8r>>SJyJdbX1g(a-l}( z?(7tV%t%d*JA$O1?~?rRTL7EC2vINQs9 zK%OEAgN9nOChx7gmvWGd!K|yOs34UVNCw7b)EQd&3 zR1nF}=J!+I#yCCq1F8`y$L$;(UUhXrnoXTs8UJby#td46KHP@ZG`-^RQ=RvX(pQt# z$kiupF!A92a$}n@O>C-^+R_zFknQa9Tv)z4#mB~CK5SAF6k_07gatvl{afBc|=))M7lE5|D51;_wK3ALk!H*zoim2+sf(PO6?j^&S7x=JQ5SCujiup^OLW0 zR@-#5C7RY6vka>Gd=#jyRwr=zUrRn{f3 z2OxS z-W!oYw%^C+p`ots`8_Khv%UYBL8}=@Yr+?C12w5Z3#?$fFfFOQ`|lQRyZwNeYdL2- z&e4CkKxZ4C3dCVJK??Cjbx-=~KiIpY{TkYb&w|0hp`nP$v$*{TbLzJBILmxSckATO zdDKJI@&B5)1_n>}P;5W)ZGd}yZ7qUd0MIk8A9fsSMETx~4W$rAp_B0@5oW<|MF2$( zCn*+Oz9>eOg3)Lh^a8){@KaQ@wfzQi`%M6(@B{|R35Pkly~ln)UU{HA1X+3*oL6Y~ zj$3y`;Ga}gRS%m~fS78BtAs*k~>Z5Iiuy+-V>AajA4|G~+blL&cVitr!|Dwy0Ubw;vb0DTz-cZg1@sJv@G zK|w(h>4O}PXG%;|lsV~a14~+I>7ujuMv_VcQF9WZM8HEiP+)unvu=n= z(Pm+zs3n;2RWDf~-D3ETfsr9hjYK2g#tj}xxRP`=czh&_nlUIq)6ZB`b?5Hg7SLf~ z7Kb*yqX_#;Wu4FM`%ZESfQ+uBqy&MH^jM;KBWaQXW#vIt>Fs5pr*{EQ*c<=jD{-8o z@C3rjK{6xNT*AW8Bty@(wehVt6CryO)g=bp7d$7K$G;s`M^1u!cKbENig08v66qSP zJcIWyI0{8c-`QF0fqIK~pFrw#XR;~mOO_iI&(fRmC5p?TN!OAdVPa=bOi5|Pj!LTK zePSj-HE!ct9V)5QvwH}@m zxq2)KtO1!YXz4u=Tu5aHj-=A$B$ho%XvR<^7~P4u9-z8DI5e~cITmX1n{IB(N=hA* zOLcX}E&bw^d`YkYlx(Yx&Nuk;%t=04|Nb4)PWI?G%&uFJJ+>OoT{%Bw7#Z2?nme{yo!M`P zBE&e3pMqpOP}Q%`msn?3dMOY96_saTWH9EuuAHhbEjs9x(ALuX78#e{dzH5+2VNOA zVmc|gRE~!-ID=FV33nf1A7MHC9U0ag2HE0_P|U?gN1quVhXszQSp5Bf0J-H?N51xU zL7@Z0CMql`v+=oT#LknJ0uu-uUp(*P9IW3&7x3ad&gb{@X|i12XI2`u)SRmvFgu%Y1Vo^xA@`VjYVLxe z1|d1J8+U03iWQQ24>bKy72Z^HK6fTYM#Xy*Ow=#HPlqFC#{)Tb;RhihCp;F?#24Jq zEkivJtok=_u@u!#bSD(6H*3u{KbSfvzP>E!u9aOcZIpmb4+Y23WtNDXWjYC8l*5O^ z6NJIfH!7~Ibi+9L=JxEi8VhU1>)aC=Pj;h_i+gxYCdU0CXvk-uC8kEV$gJ^&2OCT!~ODdKs8l?yOx^QDq5kRA4Db5Cg zL$+q(CBsXfZ_O5>5<`Cq!ph>z!2#?#h(=XgT^;j51T54gnd0fF>{oyebxkfo&3gaF z`0x1<$o`Z$bIioNAua{5A``}2*wKqB9Q(Hk#CHb5U&phSKk-zVGjk+&JK5E+@fF4>>4yEsV@{L*_defdYUYMmjRvtTYrq{K|u7}NtbvT@G6=w7)r6HJ=LtQuZI_hmZY~$ zr9BKQNFsLdE1n)rUNxI zydkF>MJLi8(D!GcoIFlSg4q%zR;1i9x1}3TJk67b4Pm1 z6@v7I{*IxJ?udtXc!6qmT>J)K5&fOuB@)4pug_~ zZqqcFcJcMB1ecLc@)yhRxx<(}!X`!}i^U)8q^2ebc9Ava?#d6ZlRk6%&zB5*J+F?f zZc}1Y_~XbhX%&7_Wpu#1_BI(|AWP`iR`%Di^OWU@{dToY(-SSRUuI`dfb}XoL_?@) zs`HJp@h!jIR5q6AN%<)Id;XtPdIEv%Rh|63;StF%KN4HKUiO?DAIu-eobK&;K^xEV z{xSW)R3l$Y(lp8^AB@KZOrr$CGXI;-W$?ZI#U0LkOW!wJFqbJ1UY+qO2P~~6gD0qp zL2wd{6esy|x-jJ45D({GDi$UU9c_m&0e5v7` zgIY%{EMlNC^w(pS3G>HH?3oQWYSm&gI&ET_M{1nLe+nP5TFu~$#s-hDy%nXnY7gUC zNn{JNFXKDjrJ4I<7YxmVi;D`D6qwhY>XZC>O2&#ZbwVJ_%3AUd)n@QvytGJYfwO&A zmnY?@!_=$GDhI9p^ENF~BX506+qyT@U&=r{(IYzd5XW;4KB$5Uo_$|wXpvXvTVuh{ zuI7E>l+4S4__$6C5n+vH{Pc_jzN@?J*=o~1XP1}M$M+QvOa$g)L{}!TAZ46*+{lXZ zjcwZk1HM_M;G%IVrilJJ%pNIW(j1FBNT0YvD-}AOmi3!*+D|85Ulx(a#Lw_VAJYr9 zEUDIogL`_eQKTf-Hy$}+{Xpcvb766Mel7AdlD2Af6b7f2R2QYzTgtx9GO^Nq+qkxv z(Da7VX4S8%ou>U)PrtLQu1xl7t#UW-M#D&V@O-a&)xOCm({=H6db4@~ts+kcPX_ke z3Ou^}$J?aguweXP$?SF-)ulJWK{oP{6;EQ-!U=q($=NslhNbT>!TC}oJw00b87~Br|mx@+UZnGw6<-=-~16?Fh753 zd176-c(2IN5L=PHZ;r41>D}`o(>1Al?AZf4d=we}p0|%Hgmgc54PNsJX%BHy6;ZCz zIJ06XC@foAM_Fz%(s#_yub^14t3l@x>quQFrS0~i!oizBh!o1`>*|sm+ zsYg=#;pG9Je{~#QKmYt>zRj~&@p9Ygfbr`t2`-fBx6Fp$J(a%H?5yhMk$PY`rj303 zGs_5L(o2KW9;{y@SgA9L$sd$o;V<&DUEIxQlJm|_@YdjioTybcGqny^pX?_A?2-YC zOG{y4Q@g0_B69siq~2e7pHoyO=}3$v(o`=ED$(UMok}Te&TgAG`cd{mTj^JGhwWzquWr?lU*Y=^KCbE6Dh#sL^JF$AwFu$_Lsr9a<@M-F~{WD*7{A>2xjG7k{Ye zU;O*0O%%6oy_V~_S{Zih$-lh2kGs>_<_z8s3JzI3X$)C75H7!3%UpV$!Q^st$ZAil zTZZB;h7_lytkik7_47qF-v?JjGCs2|^STB;U#uEcm1(;0MtT4JndX!lxi=TNM^w5N zUwGz!{{1(;@WP(Q9}m<0>$xP_@53(Hrd@Mg_T>J-Jf%PO;h7O3v-9)n*o*2>6!DA6 z{qTSFOmE6(-k$z-blQUE+l>l-rwR4F)1Ijl9;Mv}Qo@_epFeKgqPf4*R3_};Kv@I* z1D3F9&#tA`f?hSyv7--dgzufcr*2I5@aK`Hrc=Bszh*T)=I@CUdTV8WZNSLv_Mb|z z$UW8OV{NMgF0<5E29EYsEUW#Ex+Z;9E~@DzuhV4;E~h1fMl};Q(eKUiA43_2qiarl zUX|ABdstjNw%egUYxTpQ;l#|Q^c9PtyIB+a57q!d7b*Rx689PA4B|wcRVeLi@5Nv`Rrt{UgpN?lo*4L=}3yzp`mkIVtuTu zx!bK$*a^?lS}91Rjj>0R=b{lu|93P={~tevKuQ|_FFy=Jwzvp{t*zVNOJ(lUjsC?O O2 Date: Thu, 8 Dec 2022 12:12:09 +0000 Subject: [PATCH 21/44] docs: added documentation for client state methods. (#2886) --- docs/ibc/light-clients/client-state.md | 74 +++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/docs/ibc/light-clients/client-state.md b/docs/ibc/light-clients/client-state.md index cfb3805268f..cff2fd23f21 100644 --- a/docs/ibc/light-clients/client-state.md +++ b/docs/ibc/light-clients/client-state.md @@ -1,3 +1,75 @@ \ No newline at end of file +--> + +# Implementing the `ClientState` interface + +Learn how to implement the [`ClientState`](https://github.com/cosmos/ibc-go/blob/v6.0.0-rc1/modules/core/exported/client.go#L40) interface. + +## `ClientType` method + +`ClientType` should return a unique string identifier of the light client. This will be used when generating a client identifier. +The format is created as follows: `ClientType-{N}` where `{N}` is the unique global nonce associated with a specific client. + +## `GetLatestHeight` method + +`GetLatestHeight` should return the latest block height that the client state represents. + +## `Validate` method + +`Validate` should validate every client state field and should return an error if any value is invalid. The light client +implementor is in charge of determining which checks are required. See the [tendermint light client implementation](https://github.com/cosmos/ibc-go/blob/v6.0.0-rc1/modules/light-clients/07-tendermint/types/client_state.go#L101) +as a reference. + +## `Status` method + +`Status` must return the status of the client. + +- An `Active` status indicates that clients are allowed to process packets. +- A `Frozen` status indicates that a client is not allowed to be used. +- An `Expired` status indicates that a client is not allowed to be used. +- An `Unknown` status indicates that there was an error in determining the status of a client. + +All possible Status types can be found [here](https://github.com/cosmos/ibc-go/blob/v6.0.0-rc1/modules/core/exported/client.go#L26-L36). + +This field is returned by the gRPC [QueryClientStatusResponse](https://github.com/cosmos/ibc-go/blob/v6.0.0-rc1/modules/core/02-client/types/query.pb.go#L665) endpoint. + +## `ZeroCustomFields` method + +`ZeroCustomFields` should return a copy of the light client with all client customizable fields with their zero value. It should not mutate the fields of the light client. +This method is used when [scheduling upgrades](https://github.com/cosmos/ibc-go/blob/v6.0.0-rc1/modules/core/02-client/keeper/proposal.go#L89). Upgrades are used to upgrade chain specific fields. +In the tendermint case, this may be the chainID or the unbonding period. +For more information about client upgrades see [the developer guide](../upgrades/developer-guide.md). + +## `GetTimestampAtHeight` method + +`GetTimestampAtHeight` must return the timestamp for the consensus state associated with the provided height. +This value is used to facilitate timeouts by checking the packet timeout timestamp against the returned value. + +## `Initialize` method + +Clients must validate the initial consensus state, and may store any client-specific metadata necessary. + +`Initialize` gets called when a [client is created](https://github.com/cosmos/ibc-go/blob/main/modules/core/02-client/keeper/client.go#L32). + +## `VerifyMembership` method + +`VerifyMembership` must verify the existence of a value at a given CommitmentPath at the specified height. For more information about membership proofs +see [the proof docs](./proofs.md). + +## `VerifyNonMembership` method + +`VerifyNonMembership` must verify the absence of a value at a given CommitmentPath at a specified height. For more information about non membership proofs +see [the proof docs](./proofs.md). + +## `VerifyClientMessage` method + +VerifyClientMessage must verify a ClientMessage. A ClientMessage could be a Header, Misbehaviour, or batch update. +It must handle each type of ClientMessage appropriately. Calls to CheckForMisbehaviour, UpdateState, and UpdateStateOnMisbehaviour +will assume that the content of the ClientMessage has been verified and can be trusted. An error should be returned +if the ClientMessage fails to verify. + +## `CheckForMisbehaviour` method + +Checks for evidence of a misbehaviour in Header or Misbehaviour type. It assumes the ClientMessage +has already been verified. From a25a9d04d3c014b619ec4c1626c288fe731eb8f8 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Thu, 8 Dec 2022 16:00:44 +0100 Subject: [PATCH 22/44] docs: improvements to ICA docs --- docs/.vuepress/config.js | 37 +- .../interchain-accounts/active-channels.md | 18 +- docs/apps/interchain-accounts/auth-modules.md | 396 +----------------- docs/apps/interchain-accounts/client.md | 180 ++++++++ docs/apps/interchain-accounts/development.md | 16 +- docs/apps/interchain-accounts/integration.md | 68 +-- docs/apps/interchain-accounts/messages.md | 61 +-- docs/apps/interchain-accounts/overview.md | 8 +- docs/apps/interchain-accounts/parameters.md | 19 +- docs/assets/ica/ica-v6.png | Bin 0 -> 163580 bytes .../controller/keeper/account.go | 2 +- 11 files changed, 295 insertions(+), 510 deletions(-) create mode 100644 docs/apps/interchain-accounts/client.md create mode 100644 docs/assets/ica/ica-v6.png diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index d89efb89ac6..aead1124617 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -218,6 +218,16 @@ module.exports = { directory: false, path: "/apps/interchain-accounts/overview.html", }, + { + title: "Development Use Cases", + directory: false, + path: "/apps/interchain-accounts/development.html", + }, + { + title: "Authentication Modules", + directory: false, + path: "/apps/interchain-accounts/auth-modules.html", + }, { title: "Integration", directory: false, @@ -234,9 +244,9 @@ module.exports = { path: "/apps/interchain-accounts/parameters.html", }, { - title: "Development Use Cases", + title: "Client", directory: false, - path: "/apps/interchain-accounts/development.html", + path: "/apps/interchain-accounts/client.html", }, { title: "Active Channels", @@ -244,9 +254,26 @@ module.exports = { path: "/apps/interchain-accounts/active-channels.html", }, { - title: "Authentication Modules", - directory: false, - path: "/apps/interchain-accounts/auth-modules.html", + title: "Legacy", + directory: true, + path: "/apps/interchain-accounts", + children: [ + { + title: "Authentication Modules", + directory: false, + path: "/apps/interchain-accounts/legacy/auth-modules.html", + }, + { + title: "Integration", + directory: false, + path: "/apps/interchain-accounts/legacy/integration.html", + }, + { + title: "Keeper API", + directory: false, + path: "/apps/interchain-accounts/legacy/keeper-api.html", + }, + ] }, ], }, diff --git a/docs/apps/interchain-accounts/active-channels.md b/docs/apps/interchain-accounts/active-channels.md index bce6db92a9c..4163f00d39a 100644 --- a/docs/apps/interchain-accounts/active-channels.md +++ b/docs/apps/interchain-accounts/active-channels.md @@ -1,5 +1,5 @@ # Understanding Active Channels @@ -7,21 +7,25 @@ order: 6 The Interchain Accounts module uses [ORDERED channels](https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#ordering) to maintain the order of transactions when sending packets from a controller to a host chain. A limitation when using ORDERED channels is that when a packet times out the channel will be closed. In the case of a channel closing, a controller chain needs to be able to regain access to the interchain account registered on this channel. `Active Channels` enable this functionality. -When an Interchain Account is registered using `MsgRegisterInterchainAccount`, a new channel is created on a particular port. During the `OnChanOpenAck` and `OnChanOpenConfirm` steps (controller & host chain) the `Active Channel` for this interchain account -is stored in state. + +When an Interchain Account is registered using `MsgRegisterInterchainAccount`, a new channel is created on a particular port. During the `OnChanOpenAck` and `OnChanOpenConfirm` steps (on controller & host chain respectively) the `Active Channel` for this interchain account is stored in state. It is possible to create a new channel using the same controller chain portID if the previously set `Active Channel` is now in a `CLOSED` state. This channel creation can be initialized programatically by sending a new `MsgChannelOpenInit` message like so: ```go -msg := channeltypes.NewMsgChannelOpenInit(portID, string(versionBytes), channeltypes.ORDERED, []string{connectionID}, icatypes.PortID, icatypes.ModuleName) -handler := k.msgRouter.Handler(msg) +msg := channeltypes.NewMsgChannelOpenInit(portID, string(versionBytes), channeltypes.ORDERED, []string{connectionID}, icatypes.HostPortID, authtypes.NewModuleAddress(icatypes.ModuleName).String()) +handler := keeper.msgRouter.Handler(msg) +res, err := handler(ctx, msg) +if err != nil { + return err +} ``` Alternatively, any relayer operator may initiate a new channel handshake for this interchain account once the previously set `Active Channel` is in a `CLOSED` state. This is done by initiating the channel handshake on the controller chain using the same portID associated with the interchain account in question. -It is important to note that once a channel has been opened for a given Interchain Account, new channels can not be opened for this account until the currently set `Active Channel` is set to `CLOSED`. +It is important to note that once a channel has been opened for a given interchain account, new channels can not be opened for this account until the currently set `Active Channel` is set to `CLOSED`. -## Future Improvements +## Future improvements Future versions of the ICS-27 protocol and the Interchain Accounts module will likely use a new channel type that provides ordering of packets without the channel closing in the event of a packet timing out, thus removing the need for `Active Channels` entirely. The following is a list of issues which will provide the infrastructure to make this possible: diff --git a/docs/apps/interchain-accounts/auth-modules.md b/docs/apps/interchain-accounts/auth-modules.md index 65fb30010a3..c53230f69d8 100644 --- a/docs/apps/interchain-accounts/auth-modules.md +++ b/docs/apps/interchain-accounts/auth-modules.md @@ -1,399 +1,21 @@ # Building an authentication module -### Deprecation Notice +Authentication modules enable application developers to perform custom logic when interacting with the Interchain Accounts controller sumbmodule's `MsgServer`. {synopsis} -**This document is deprecated and will be removed in future releases**. +The controller submodule is used for account registration and packet sending. It executes only logic required of all controllers of interchain accounts. The type of authentication used to manage the interchain accounts remains unspecified. There may exist many different types of authentication which are desirable for different use cases. Thus the purpose of the authentication module is to wrap the controller submodule with custom authentication logic. -Authentication modules play the role of the `Base Application` as described in [ICS30 IBC Middleware](https://github.com/cosmos/ibc/tree/master/spec/app/ics-030-middleware), and enable application developers to perform custom logic when working with the Interchain Accounts controller API. {synopsis} - -The controller submodule is used for account registration and packet sending. -It executes only logic required of all controllers of interchain accounts. -The type of authentication used to manage the interchain accounts remains unspecified. -There may exist many different types of authentication which are desirable for different use cases. -Thus the purpose of the authentication module is to wrap the controller module with custom authentication logic. - -In ibc-go, authentication modules are connected to the controller chain via a middleware stack. -The controller module is implemented as [middleware](https://github.com/cosmos/ibc/tree/master/spec/app/ics-030-middleware) and the authentication module is connected to the controller module as the base application of the middleware stack. -To implement an authentication module, the `IBCModule` interface must be fulfilled. -By implementing the controller module as middleware, any amount of authentication modules can be created and connected to the controller module without writing redundant code. +In ibc-go, authentication modules can communicate with the controller submodule by passing messages through `baseapp`'s `MsgServiceRouter`. To implement an authentication module, the `IBCModule` interface need not be fulfilled; it is only required to fulfill Cosmos SDK's `AppModuleBasic` interface, just like any regular Cosmos SDK application module. The authentication module must: -- Authenticate interchain account owners -- Track the associated interchain account address for an owner -- Claim the channel capability in `OnChanOpenInit` -- Send packets on behalf of an owner (after authentication) - -### IBCModule implementation - -The following `IBCModule` callbacks must be implemented with appropriate custom logic: - -```go -// OnChanOpenInit implements the IBCModule interface -func (im IBCModule) OnChanOpenInit( - ctx sdk.Context, - order channeltypes.Order, - connectionHops []string, - portID string, - channelID string, - chanCap *capabilitytypes.Capability, - counterparty channeltypes.Counterparty, - version string, -) (string, error) { - // the authentication module *must* claim the channel capability on OnChanOpenInit - if err := im.keeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { - return version, err - } - - // perform custom logic - - return version, nil -} - -// OnChanOpenAck implements the IBCModule interface -func (im IBCModule) OnChanOpenAck( - ctx sdk.Context, - portID, - channelID string, - counterpartyVersion string, -) error { - // perform custom logic - - return nil -} - -// OnChanCloseConfirm implements the IBCModule interface -func (im IBCModule) OnChanCloseConfirm( - ctx sdk.Context, - portID, - channelID string, -) error { - // perform custom logic - - return nil -} - -// OnAcknowledgementPacket implements the IBCModule interface -func (im IBCModule) OnAcknowledgementPacket( - ctx sdk.Context, - packet channeltypes.Packet, - acknowledgement []byte, - relayer sdk.AccAddress, -) error { - // perform custom logic - - return nil -} - -// OnTimeoutPacket implements the IBCModule interface. -func (im IBCModule) OnTimeoutPacket( - ctx sdk.Context, - packet channeltypes.Packet, - relayer sdk.AccAddress, -) error { - // perform custom logic - - return nil -} -``` - -**Note**: The channel capability must be claimed by the authentication module in `OnChanOpenInit` otherwise the authentication module will not be able to send packets on the channel created for the associated interchain account. - -The following functions must be defined to fulfill the `IBCModule` interface, but they will never be called by the controller module so they may error or panic. - -```go -// OnChanOpenTry implements the IBCModule interface -func (im IBCModule) OnChanOpenTry( - ctx sdk.Context, - order channeltypes.Order, - connectionHops []string, - portID, - channelID string, - chanCap *capabilitytypes.Capability, - counterparty channeltypes.Counterparty, - counterpartyVersion string, -) (string, error) { - panic("UNIMPLEMENTED") -} - -// OnChanOpenConfirm implements the IBCModule interface -func (im IBCModule) OnChanOpenConfirm( - ctx sdk.Context, - portID, - channelID string, -) error { - panic("UNIMPLEMENTED") -} - -// OnChanCloseInit implements the IBCModule interface -func (im IBCModule) OnChanCloseInit( - ctx sdk.Context, - portID, - channelID string, -) error { - panic("UNIMPLEMENTED") -} - -// OnRecvPacket implements the IBCModule interface. A successful acknowledgement -// is returned if the packet data is succesfully decoded and the receive application -// logic returns without error. -func (im IBCModule) OnRecvPacket( - ctx sdk.Context, - packet channeltypes.Packet, - relayer sdk.AccAddress, -) ibcexported.Acknowledgement { - panic("UNIMPLEMENTED") -} -``` - -### Legacy API - -## `RegisterInterchainAccount` - -The authentication module can begin registering interchain accounts by calling `RegisterInterchainAccount`: - -```go -if err := keeper.icaControllerKeeper.RegisterInterchainAccount(ctx, connectionID, owner.String(), version); err != nil { - return err -} - -return nil -``` - -The `version` argument is used to support ICS29 fee middleware for relayer incentivization of ICS27 packets. Consumers of the `RegisterInterchainAccount` are expected to build the appropriate JSON encoded version string themselves and pass it accordingly. If an empty string is passed in the `version` argument, then the version will be initialized to a default value in the `OnChanOpenInit` callback of the controller's handler, so that channel handshake can proceed. - -The following code snippet illustrates how to construct an appropriate interchain accounts `Metadata` and encode it as a JSON bytestring: - -```go -icaMetadata := icatypes.Metadata{ - Version: icatypes.Version, - ControllerConnectionId: controllerConnectionID, - HostConnectionId: hostConnectionID, - Encoding: icatypes.EncodingProtobuf, - TxType: icatypes.TxTypeSDKMultiMsg, -} - -appVersion, err := icatypes.ModuleCdc.MarshalJSON(&icaMetadata) -if err != nil { - return err -} - -if err := keeper.icaControllerKeeper.RegisterInterchainAccount(ctx, controllerConnectionID, owner.String(), string(appVersion)); err != nil { - return err -} -``` - -Similarly, if the application stack is configured to route through ICS29 fee middleware and a fee enabled channel is desired, construct the appropriate ICS29 `Metadata` type: - -```go -icaMetadata := icatypes.Metadata{ - Version: icatypes.Version, - ControllerConnectionId: controllerConnectionID, - HostConnectionId: hostConnectionID, - Encoding: icatypes.EncodingProtobuf, - TxType: icatypes.TxTypeSDKMultiMsg, -} - -appVersion, err := icatypes.ModuleCdc.MarshalJSON(&icaMetadata) -if err != nil { - return err -} - -feeMetadata := feetypes.Metadata{ - AppVersion: string(appVersion), - FeeVersion: feetypes.Version, -} - -feeEnabledVersion, err := feetypes.ModuleCdc.MarshalJSON(&feeMetadata) -if err != nil { - return err -} - -if err := keeper.icaControllerKeeper.RegisterInterchainAccount(ctx, controllerConnectionID, owner.String(), string(feeEnabledVersion)); err != nil { - return err -} -``` - -## `SendTx` - -The authentication module can attempt to send a packet by calling `SendTx`: -```go - -// Authenticate owner -// perform custom logic - -// Construct controller portID based on interchain account owner address -portID, err := icatypes.NewControllerPortID(owner.String()) -if err != nil { - return err -} - -channelID, found := keeper.icaControllerKeeper.GetActiveChannelID(ctx, portID) -if !found { - return sdkerrors.Wrapf(icatypes.ErrActiveChannelNotFound, "failed to retrieve active channel for port %s", portID) -} - -// Obtain the channel capability, claimed in OnChanOpenInit -chanCap, found := keeper.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(portID, channelID)) -if !found { - return sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability") -} - -// Obtain data to be sent to the host chain. -// In this example, the owner of the interchain account would like to send a bank MsgSend to the host chain. -// The appropriate serialization function should be called. The host chain must be able to deserialize the transaction. -// If the host chain is using the ibc-go host module, `SerializeCosmosTx` should be used. -msg := &banktypes.MsgSend{FromAddress: fromAddr, ToAddress: toAddr, Amount: amt} -data, err := icatypes.SerializeCosmosTx(keeper.cdc, []proto.Message{msg}) -if err != nil { - return err -} - -// Construct packet data -packetData := icatypes.InterchainAccountPacketData{ - Type: icatypes.EXECUTE_TX, - Data: data, -} - -// Obtain timeout timestamp -// An appropriate timeout timestamp must be determined based on the usage of the interchain account. -// If the packet times out, the channel will be closed requiring a new channel to be created -timeoutTimestamp := obtainTimeoutTimestamp() - -// Send the interchain accounts packet, returning the packet sequence -seq, err = keeper.icaControllerKeeper.SendTx(ctx, chanCap, portID, packetData, timeoutTimestamp) -``` - -The data within an `InterchainAccountPacketData` must be serialized using a format supported by the host chain. -If the host chain is using the ibc-go host chain submodule, `SerializeCosmosTx` should be used. If the `InterchainAccountPacketData.Data` is serialized using a format not support by the host chain, the packet will not be successfully received. - -## `OnAcknowledgementPacket` - -Controller chains will be able to access the acknowledgement written into the host chain state once a relayer relays the acknowledgement. -The acknowledgement bytes will be passed to the auth module via the `OnAcknowledgementPacket` callback. -Auth modules are expected to know how to decode the acknowledgement. - -If the controller chain is connected to a host chain using the host module on ibc-go, it may interpret the acknowledgement bytes as follows: - -Begin by unmarshaling the acknowledgement into `sdk.TxMsgData`: -```go -var ack channeltypes.Acknowledgement -if err := channeltypes.SubModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil { - return err -} - -txMsgData := &sdk.TxMsgData{} -if err := proto.Unmarshal(ack.GetResult(), txMsgData); err != nil { - return err -} -``` - -If the `txMsgData.Data` field is non nil, the host chain is using SDK version <= v0.45. -The auth module should interpret the `txMsgData.Data` as follows: - -```go -switch len(txMsgData.Data) { -case 0: - // see documentation below for SDK 0.46.x or greater -default: - for _, msgData := range txMsgData.Data { - if err := handler(msgData); err != nil { - return err - } - } -... -} -``` - -A handler will be needed to interpret what actions to perform based on the message type sent. -A router could be used, or more simply a switch statement. - -```go -func handler(msgData sdk.MsgData) error { -switch msgData.MsgType { -case sdk.MsgTypeURL(&banktypes.MsgSend{}): - msgResponse := &banktypes.MsgSendResponse{} - if err := proto.Unmarshal(msgData.Data, msgResponse}; err != nil { - return err - } - - handleBankSendMsg(msgResponse) - -case sdk.MsgTypeURL(&stakingtypes.MsgDelegate{}): - msgResponse := &stakingtypes.MsgDelegateResponse{} - if err := proto.Unmarshal(msgData.Data, msgResponse}; err != nil { - return err - } - - handleStakingDelegateMsg(msgResponse) - -case sdk.MsgTypeURL(&transfertypes.MsgTransfer{}): - msgResponse := &transfertypes.MsgTransferResponse{} - if err := proto.Unmarshal(msgData.Data, msgResponse}; err != nil { - return err - } - - handleIBCTransferMsg(msgResponse) - -default: - return -} -``` - -If the `txMsgData.Data` is empty, the host chain is using SDK version > v0.45. -The auth module should interpret the `txMsgData.Responses` as follows: - -```go -... -// switch statement from above -case 0: - for _, any := range txMsgData.MsgResponses { - if err := handleAny(any); err != nil { - return err - } - } -} -``` - -A handler will be needed to interpret what actions to perform based on the type url of the Any. -A router could be used, or more simply a switch statement. -It may be possible to deduplicate logic between `handler` and `handleAny`. - -```go -func handleAny(any *codectypes.Any) error { -switch any.TypeURL { -case banktypes.MsgSend: - msgResponse, err := unpackBankMsgSendResponse(any) - if err != nil { - return err - } - - handleBankSendMsg(msgResponse) - -case stakingtypes.MsgDelegate: - msgResponse, err := unpackStakingDelegateResponse(any) - if err != nil { - return err - } - - handleStakingDelegateMsg(msgResponse) - - case transfertypes.MsgTransfer: - msgResponse, err := unpackIBCTransferMsgResponse(any) - if err != nil { - return err - } - handleIBCTransferMsg(msgResponse) - -default: - return -} -``` +- Authenticate interchain account owners. +- Track the associated interchain account address for an owner. +- Send packets on behalf of an owner (after authentication). -### Integration into `app.go` file +## Integration into `app.go` file -To integrate the authentication module into your chain, please follow the steps outlined above in [app.go integration](./integration.md#example-integration). +To integrate the authentication module into your chain, please follow the steps outlined in [`app.go` integration](./integration.md#example-integration). diff --git a/docs/apps/interchain-accounts/client.md b/docs/apps/interchain-accounts/client.md new file mode 100644 index 00000000000..f34f5243cdc --- /dev/null +++ b/docs/apps/interchain-accounts/client.md @@ -0,0 +1,180 @@ + + +# Client + +## CLI + +A user can query and interact with the Interchain Accounts module using the CLI. Use the `--help` flag to discover the available commands: + +```shell +simd query interchain-accounts --help +``` + +> Please not that this section does not document all the available commands, but only the ones that deserved extra documentation that was not possible to fit in the command line documentation. + +### Controller + +A user can query and interact with the controller submodule. + +#### Query + +The `query` commands allow users to query the controller submodule. + +```shell +simd query interchain-accounts controller --help +``` + +#### Transactions + +The `tx` commands allow users to interact with the controller submodule. + +```shell +simd tx interchain-accounts controller --help +``` + +#### `send-tx` + +The `send-tx` command allows users to send a transaction on the provided connection to be executed using an interchain account on the host chain. + +```shell +simd tx interchain-accounts controller send-tx [connection-id] [path/to/packet_msg.json] +``` + +Example: + +```shell +simd tx interchain-accounts controller send-tx connection-0 packet-data.json --from cosmos1.. +``` + +See below for example contents of `packet-data.json`. The CLI handler will unmarshal the following into `InterchainAccountPacketData` appropriately. + +```json +{ + "type":"TYPE_EXECUTE_TX", + "data":"CqIBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEoEBCkFjb3Ntb3MxNWNjc2hobXAwZ3N4MjlxcHFxNmc0em1sdG5udmdteXU5dWV1YWRoOXkybmM1emowc3psczVndGRkehItY29zbW9zMTBoOXN0YzV2Nm50Z2V5Z2Y1eGY5NDVuanFxNWgzMnI1M3VxdXZ3Gg0KBXN0YWtlEgQxMDAw", + "memo":"" +} +``` + +Note the `data` field is a base64 encoded byte string as per the [proto3 JSON encoding specification](https://developers.google.com/protocol-buffers/docs/proto3#json). + +A helper CLI is provided in the host submodule which can be used to generate the packet data JSON using the counterparty chain's binary. See the [`generate-packet-data` command](#generate-packet-data) for an example. + +### Host + +A user can query and interact with the host submodule. + +#### Query + +The `query` commands allow users to query the host submodule. + +```shell +simd query interchain-accounts host --help +``` + +#### Transactions + +The `tx` commands allow users to interact with the controller submodule. + +```shell +simd tx interchain-accounts host --help +``` + +##### `generate-packet-data` + +The `generate-packet-data` command allows users to generate interchain accounts packet data for input message(s). The packet data can then be used with the controller submodule's [`send-tx` command](#send-tx). + +```shell +simd tx interchain-accounts host generate-packet-data [message] +``` + +Example: + +```shell +simd tx interchain-accounts host generate-packet-data '[{ + "@type":"/cosmos.bank.v1beta1.MsgSend", + "from_address":"cosmos15ccshhmp0gsx29qpqq6g4zmltnnvgmyu9ueuadh9y2nc5zj0szls5gtddz", + "to_address":"cosmos10h9stc5v6ntgeygf5xf945njqq5h32r53uquvw", + "amount": [ + { + "denom": "stake", + "amount": "1000" + } + ] +}]' --memo memo +``` + +The command accepts a single `sdk.Msg` or a list of `sdk.Msg`s that will be encoded into the outputs `data` field. + +Example output: + +```json +{ + "type":"TYPE_EXECUTE_TX", + "data":"CqIBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEoEBCkFjb3Ntb3MxNWNjc2hobXAwZ3N4MjlxcHFxNmc0em1sdG5udmdteXU5dWV1YWRoOXkybmM1emowc3psczVndGRkehItY29zbW9zMTBoOXN0YzV2Nm50Z2V5Z2Y1eGY5NDVuanFxNWgzMnI1M3VxdXZ3Gg0KBXN0YWtlEgQxMDAw", + "memo":"memo" +} +``` + +## gRPC + +A user can query the interchain account module using gRPC endpoints. + +### Controller + +A user can query the controller submodule using gRPC endpoints. + +#### `InterchainAccount` + +The `InterchainAccount` endpoint allows users to query the controller submodule for the interchain account address for a given owner on a particular connection. + +```shell +ibc.applications.interchain_accounts.controller.v1.Query/InterchainAccount +``` + +Example: + +``` +grpcurl -plaintext \ + -d '{"owner":"cosmos1..","connection_id":"connection-0"}' \ + localhost:9090 \ + ibc.applications.interchain_accounts.controller.v1.Query/InterchainAccount +``` + +#### `Params` + +The `Params` endpoint users to query the current controller submodule parameters. + +```shell +ibc.applications.interchain_accounts.controller.v1.Query/Params +``` + +Example: + +```shell +grpcurl -plaintext \ + localhost:9090 \ + ibc.applications.interchain_accounts.controller.v1.Query/Params +``` + +### Host + +A user can query the host submodule using gRPC endpoints. + +#### `Params` + +The `Params` endpoint users to query the current host submodule parameters. + +```shell +ibc.applications.interchain_accounts.host.v1.Query/Params +``` + +Example: + +```shell +grpcurl -plaintext \ + localhost:9090 \ + ibc.applications.interchain_accounts.host.v1.Query/Params +``` diff --git a/docs/apps/interchain-accounts/development.md b/docs/apps/interchain-accounts/development.md index 3d791a97405..6543caeafe6 100644 --- a/docs/apps/interchain-accounts/development.md +++ b/docs/apps/interchain-accounts/development.md @@ -1,24 +1,24 @@ # Development use cases -The initial version of interchain accounts allowed for the controller module to be extended by providing it with an underlying application which would handle all packet callbacks. +The initial version of Interchain Accounts allowed for the controller submodule to be extended by providing it with an underlying application which would handle all packet callbacks. That functionality is now being deprecated in favor of alternative approaches. This document will outline potential use cases and redirect each use case to the appropriate documentation. ## Custom authentication Interchain accounts may be associated with alternative types of authentication relative to the traditional public/private key signing. -If you wish to develop or use interchain accounts with a custom authentication module, we recommend you use ibc-go v6 or greater. +If you wish to develop or use Interchain Accounts with a custom authentication module and do not need to execute custom logic on the packet callbacks, we recommend you use ibc-go v6 or greater and that your custom authentication module interacts with the controller submodule via the [`MsgServer`](./messages.md). -The custom authentication module should interact with the controller module via the [MsgServer](./messages.md). +If you wish to consume and execute custom logic in the packet callbacks, then please read the section [Packet callbacks](#packet-callbacks) below. ## Redirection to a smart contract It may be desirable to allow smart contracts to control an interchain account. -To faciliate such an action, the controller module may be provided an underlying application which redirects to smart contract callers. +To faciliate such an action, the controller submodule may be provided an underlying application which redirects to smart contract callers. An improved design has been suggested in [ADR 008](https://github.com/cosmos/ibc-go/pull/1976) which performs this action via middleware. Implementors of this use case are recommended to follow the ADR 008 approach. @@ -26,11 +26,11 @@ The underlying application may continue to be used as a short term solution for ## Packet callbacks -If a developer requires access to packet callbacks for their use case, then they having the following options: +If a developer requires access to packet callbacks for their use case, then they have the following options: 1. Write a smart contract which is connected via an ADR 008 or equivalent IBC application (recommended). 2. Use the controller's underlying application to implement packet callback logic. -If the first case, the smart contract should use the [MsgServer](./messages.md). +In the first case, the smart contract should use the [`MsgServer`](./messages.md). -In the second case, the underlying application should use the [legacy API](./auth-modules.md#registerinterchainaccount). +In the second case, the underlying application should use the [legacy API](./legacy/keeper-api.md). diff --git a/docs/apps/interchain-accounts/integration.md b/docs/apps/interchain-accounts/integration.md index f1139d31df2..367c0985221 100644 --- a/docs/apps/interchain-accounts/integration.md +++ b/docs/apps/interchain-accounts/integration.md @@ -1,5 +1,5 @@ # Integration @@ -8,12 +8,13 @@ Learn how to integrate Interchain Accounts host and controller functionality to The Interchain Accounts module contains two submodules. Each submodule has its own IBC application. The Interchain Accounts module should be registered as an `AppModule` in the same way all SDK modules are registered on a chain, but each submodule should create its own `IBCModule` as necessary. A route should be added to the IBC router for each submodule which will be used. -Chains who wish to support ICS27 may elect to act as a host chain, a controller chain or both. Disabling host or controller functionality may be done statically by excluding the host or controller module entirely from the `app.go` file or it may be done dynamically by taking advantage of the on-chain parameters which enable or disable the host or controller submodules. +Chains who wish to support ICS-27 may elect to act as a host chain, a controller chain or both. Disabling host or controller functionality may be done statically by excluding the host or controller submodule entirely from the `app.go` file or it may be done dynamically by taking advantage of the on-chain parameters which enable or disable the host or controller submodules. -Interchain Account authentication modules are the base application of a middleware stack. The controller submodule is the middleware in this stack. +Interchain Account authentication modules (both custom or generic, such as the `x/gov`, `x/group` or `x/auth` Cosmos SDK modules) can send messages to the controller submodule's [`MsgServer`](./messages.md) to register interchain accounts and send packets to the interchain account. To accomplish this, the authentication module needs to be composed with `baseapp`'s `MsgServiceRouter`. +![ICAv6](../../assets/ica/ica-v6.png) -### Example integration +## Example integration ```go // app.go @@ -73,40 +74,35 @@ scopedICAAuthKeeper := app.CapabilityKeeper.ScopeToModule(icaauthtypes.ModuleNam // Create the Keeper for each submodule app.ICAControllerKeeper = icacontrollerkeeper.NewKeeper( - appCodec, keys[icacontrollertypes.StoreKey], app.GetSubspace(icacontrollertypes.SubModuleName), - app.IBCKeeper.ChannelKeeper, // may be replaced with middleware such as ics29 fee - app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, - scopedICAControllerKeeper, app.MsgServiceRouter(), + appCodec, keys[icacontrollertypes.StoreKey], app.GetSubspace(icacontrollertypes.SubModuleName), + app.IBCKeeper.ChannelKeeper, // may be replaced with middleware such as ics29 fee + app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, + scopedICAControllerKeeper, app.MsgServiceRouter(), ) app.ICAHostKeeper = icahostkeeper.NewKeeper( - appCodec, keys[icahosttypes.StoreKey], app.GetSubspace(icahosttypes.SubModuleName), - app.IBCKeeper.ChannelKeeper, // may be replaced with middleware such as ics29 fee - app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, - app.AccountKeeper, scopedICAHostKeeper, app.MsgServiceRouter(), + appCodec, keys[icahosttypes.StoreKey], app.GetSubspace(icahosttypes.SubModuleName), + app.IBCKeeper.ChannelKeeper, // may be replaced with middleware such as ics29 fee + app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, + app.AccountKeeper, scopedICAHostKeeper, app.MsgServiceRouter(), ) // Create Interchain Accounts AppModule icaModule := ica.NewAppModule(&app.ICAControllerKeeper, &app.ICAHostKeeper) // Create your Interchain Accounts authentication module -app.ICAAuthKeeper = icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], app.ICAControllerKeeper, scopedICAAuthKeeper) +app.ICAAuthKeeper = icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], app.MsgServiceRouter()) // ICA auth AppModule icaAuthModule := icaauth.NewAppModule(appCodec, app.ICAAuthKeeper) -// ICA auth IBC Module -icaAuthIBCModule := icaauth.NewIBCModule(app.ICAAuthKeeper) - // Create controller IBC application stack and host IBC module as desired -icaControllerStack := icacontroller.NewIBCMiddleware(icaAuthIBCModule, app.ICAControllerKeeper) +icaControllerStack := icacontroller.NewIBCMiddleware(nil, app.ICAControllerKeeper) icaHostIBCModule := icahost.NewIBCModule(app.ICAHostKeeper) // Register host and authentication routes ibcRouter. AddRoute(icacontrollertypes.SubModuleName, icaControllerStack). - AddRoute(icahosttypes.SubModuleName, icaHostIBCModule). - AddRoute(icaauthtypes.ModuleName, icaControllerStack) // Note, the authentication module is routed to the top level of the middleware stack - + AddRoute(icahosttypes.SubModuleName, icaHostIBCModule) ... // Register Interchain Accounts and authentication module AppModule's @@ -118,14 +114,14 @@ app.moduleManager = module.NewManager( ... -// Add fee middleware to begin blocker logic +// Add Interchain Accounts to begin blocker logic app.moduleManager.SetOrderBeginBlockers( ... icatypes.ModuleName, ... ) -// Add fee middleware to end blocker logic +// Add Interchain Accounts to end blocker logic app.moduleManager.SetOrderEndBlockers( ... icatypes.ModuleName, @@ -145,6 +141,17 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino paramsKeeper.Subspace(icahosttypes.SubModuleName) paramsKeeper.Subspace(icacontrollertypes.SubModuleName) ... +} +``` + +If no custom athentication module is needed and a generic Cosmos SDK authentication module can be used, then from the sample integration code above all references to `ICAAuthKeeper` and `icaAuthModule` can be removed. That's it, the following code would not be needed: + +```go +// Create your Interchain Accounts authentication module +app.ICAAuthKeeper = icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], app.MsgServiceRouter()) + +// ICA auth AppModule +icaAuthModule := icaauth.NewAppModule(appCodec, app.ICAAuthKeeper) ``` ### Using submodules exclusively @@ -174,16 +181,13 @@ ibcRouter.AddRoute(icahosttypes.SubModuleName, icaHostIBCModule) // Create Interchain Accounts AppModule omitting the host keeper icaModule := ica.NewAppModule(&app.ICAControllerKeeper, nil) -// Create your Interchain Accounts authentication module, setting up the Keeper, AppModule and IBCModule appropriately -app.ICAAuthKeeper = icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], app.ICAControllerKeeper, scopedICAAuthKeeper) -icaAuthModule := icaauth.NewAppModule(appCodec, app.ICAAuthKeeper) -icaAuthIBCModule := icaauth.NewIBCModule(app.ICAAuthKeeper) + +// Optionally instantiate your custom authentication module if needed, or not otherwise +... // Create controller IBC application stack -icaControllerStack := icacontroller.NewIBCMiddleware(icaAuthIBCModule, app.ICAControllerKeeper) +icaControllerStack := icacontroller.NewIBCMiddleware(nil, app.ICAControllerKeeper) -// Register controller and authentication routes -ibcRouter. - AddRoute(icacontrollertypes.SubModuleName, icaControllerStack). - AddRoute(icaauthtypes.ModuleName, icaControllerStack) // Note, the authentication module is routed to the top level of the middleware stack -``` +// Register controller route +ibcRouter.AddRoute(icacontrollertypes.SubModuleName, icaControllerStack) +``` \ No newline at end of file diff --git a/docs/apps/interchain-accounts/messages.md b/docs/apps/interchain-accounts/messages.md index e71af0e9859..125a0203f8b 100644 --- a/docs/apps/interchain-accounts/messages.md +++ b/docs/apps/interchain-accounts/messages.md @@ -1,5 +1,5 @@ # Messages @@ -23,8 +23,8 @@ This message is expected to fail if: This message will construct a new `MsgChannelOpenInit` on chain and route it to the core IBC message server to initiate the opening step of the channel handshake. -The controller module will generate a new port identifier and claim the associated port capability. The caller is expected to provide an appropriate application version string. For example, this may be an ICS27 JSON encoded [`Metadata`](https://github.com/cosmos/ibc-go/blob/v6.0.0-alpha1/proto/ibc/applications/interchain_accounts/v1/metadata.proto#L11) type or an ICS29 JSON encoded [`Metadata`](https://github.com/cosmos/ibc-go/blob/v6.0.0-alpha1/proto/ibc/applications/fee/v1/metadata.proto#L11) type with a nested application version. -If the `Version` string is omitted, the application will construct a default version string in the `OnChanOpenInit` handshake callback. +The controller submodule will generate a new port identifier and claim the associated port capability. The caller is expected to provide an appropriate application version string. For example, this may be an ICS-27 JSON encoded [`Metadata`](https://github.com/cosmos/ibc-go/blob/v6.0.0-rc0/proto/ibc/applications/interchain_accounts/v1/metadata.proto#L11) type or an ICS-29 JSON encoded [`Metadata`](https://github.com/cosmos/ibc-go/blob/v6.0.0-rc0/proto/ibc/applications/fee/v1/metadata.proto#L11) type with a nested application version. +If the `Version` string is omitted, the controller submodule will construct a default version string in the `OnChanOpenInit` handshake callback. ```go type MsgRegisterInterchainAccountResponse struct { @@ -32,15 +32,7 @@ type MsgRegisterInterchainAccountResponse struct { } ``` -The `ChannelID` is return in the message response. - -### CLI - -The following is an example usage of the controller CLI command used to register an interchain account. - -```bash -simd tx interchain-accounts controller register connection-0 --from cosmos1m9l358xunhhwds0568za49mzhvuxx9uxre5tud -``` +The `ChannelID` is returned in the message response. ## `MsgSendTx` @@ -74,50 +66,7 @@ type MsgSendTxResponse struct { The packet `Sequence` is returned in the message response. -### CLI - -The following is an example usage of the controller CLI command used to send a transaction to be executed using an interchain account on the corresponding host chain. - -```bash -simd tx interchain-accounts controller send-tx connection-0 packet-data.json --from cosmos1m9l358xunhhwds0568za49mzhvuxx9uxre5tud -``` - -See below for example contents of `packet-data.json`. The CLI handler will unmarshal the following into `InterchainAccountPacketData` appropriately. - -```json -{ - "type":"TYPE_EXECUTE_TX", - "data":"CqIBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEoEBCkFjb3Ntb3MxNWNjc2hobXAwZ3N4MjlxcHFxNmc0em1sdG5udmdteXU5dWV1YWRoOXkybmM1emowc3psczVndGRkehItY29zbW9zMTBoOXN0YzV2Nm50Z2V5Z2Y1eGY5NDVuanFxNWgzMnI1M3VxdXZ3Gg0KBXN0YWtlEgQxMDAw", - "memo":"" -} -``` - -Note the `data` field is a base64 encoded byte string as per the [proto3 JSON encoding specification](https://developers.google.com/protocol-buffers/docs/proto3#json). - -A helper CLI is provided in the host submodule which can be used to generate the packet data JSON using the counterparty chain's binary. -It accepts a list of `sdk.Msg`s which will be encoded into the outputs `data` field. - -```bash -simd tx interchain-accounts host generate-packet-data '[{ - "@type":"/cosmos.bank.v1beta1.MsgSend", - "from_address":"cosmos15ccshhmp0gsx29qpqq6g4zmltnnvgmyu9ueuadh9y2nc5zj0szls5gtddz", - "to_address":"cosmos10h9stc5v6ntgeygf5xf945njqq5h32r53uquvw", - "amount": [ - { - "denom": "stake", - "amount": "1000" - } - ] -}]' -``` - -The host submodule also provides a helper CLI to inspect the events of interchain accounts packets by providing the channel ID and packet sequence: - -```bash - simd q interchain-accounts host packet-events channel-0 100 -``` - -### Atomicity +## Atomicity As the Interchain Accounts module supports the execution of multiple transactions using the Cosmos SDK `Msg` interface, it provides the same atomicity guarantees as Cosmos SDK-based applications, leveraging the [`CacheMultiStore`](https://docs.cosmos.network/main/core/store.html#cachemultistore) architecture provided by the [`Context`](https://docs.cosmos.network/main/core/context.html) type. diff --git a/docs/apps/interchain-accounts/overview.md b/docs/apps/interchain-accounts/overview.md index 3886bc413c3..97133f15e94 100644 --- a/docs/apps/interchain-accounts/overview.md +++ b/docs/apps/interchain-accounts/overview.md @@ -22,13 +22,11 @@ Regular accounts use a private key to sign transactions. Interchain Accounts are `Interchain Account`: An account on a host chain created using the ICS-27 protocol. An interchain account has all the capabilities of a normal account. However, rather than signing transactions with a private key, a controller chain will send IBC packets to the host chain which signals what transactions the interchain account should execute. -#### Deprecated +`Authentication Module`: A custom application module on the controller chain that uses the Interchain Accounts module to build custom logic for the creation & management of interchain accounts. It can be either an IBC application module using the [legacy API](./legacy/keeper-api.md), or a regular Cosmos SDK application module sending messages to the controller submodule's `MsgServer` (this is the recommended approach from ibc-go v6 if access to packet callbacks is not needed). Please note that the legacy API will eventually be removed and IBC applications will not be able to use them in later releases. -`Authentication Module`: A custom IBC application module on the controller chain that uses the Interchain Accounts module API to build custom logic for the creation & management of interchain accounts. For a controller chain to utilize the interchain accounts module functionality, an authentication module is required. +## SDK security model -## SDK Security Model - -SDK modules on a chain are assumed to be trustworthy. For example, there are no checks to prevent an untrustworthy module from accessing the bank keeper. +SDK modules on a chain are assumed to be trustworthy. For example, there are no checks to prevent an untrustworthy module from accessing the bank keeper. The implementation of ICS-27 in ibc-go uses this assumption in its security considerations. diff --git a/docs/apps/interchain-accounts/parameters.md b/docs/apps/interchain-accounts/parameters.md index bf0b3122201..73302b45028 100644 --- a/docs/apps/interchain-accounts/parameters.md +++ b/docs/apps/interchain-accounts/parameters.md @@ -1,18 +1,18 @@ # Parameters The Interchain Accounts module contains the following on-chain parameters, logically separated for each distinct submodule: -### Controller Submodule Parameters +## Controller Submodule Parameters | Key | Type | Default Value | |------------------------|------|---------------| | `ControllerEnabled` | bool | `true` | -#### ControllerEnabled +### ControllerEnabled The `ControllerEnabled` parameter controls a chains ability to service ICS-27 controller specific logic. This includes the sending of Interchain Accounts packet data as well as the following ICS-26 callback handlers: - `OnChanOpenInit` @@ -21,26 +21,26 @@ The `ControllerEnabled` parameter controls a chains ability to service ICS-27 co - `OnAcknowledgementPacket` - `OnTimeoutPacket` -### Host Submodule Parameters +## Host Submodule Parameters | Key | Type | Default Value | |------------------------|----------|---------------| | `HostEnabled` | bool | `true` | | `AllowMessages` | []string | `["*"]` | -#### HostEnabled +### HostEnabled -The `HostEnabled` parameter controls a chains ability to service ICS27 host specific logic. This includes the following ICS-26 callback handlers: +The `HostEnabled` parameter controls a chains ability to service ICS-27 host specific logic. This includes the following ICS-26 callback handlers: - `OnChanOpenTry` - `OnChanOpenConfirm` - `OnChanCloseConfirm` - `OnRecvPacket` -#### AllowMessages +### AllowMessages -The `AllowMessages` parameter provides the ability for a chain to limit the types of messages or transactions that hosted interchain accounts are authorized to execute by defining an allowlist using the Protobuf message TypeURL format. +The `AllowMessages` parameter provides the ability for a chain to limit the types of messages or transactions that hosted interchain accounts are authorized to execute by defining an allowlist using the Protobuf message type URL format. -For example, a Cosmos SDK based chain that elects to provide hosted Interchain Accounts with the ability of governance voting and staking delegations will define its parameters as follows: +For example, a Cosmos SDK-based chain that elects to provide hosted Interchain Accounts with the ability of governance voting and staking delegations will define its parameters as follows: ``` "params": { @@ -48,6 +48,7 @@ For example, a Cosmos SDK based chain that elects to provide hosted Interchain A "allow_messages": ["/cosmos.staking.v1beta1.MsgDelegate", "/cosmos.gov.v1beta1.MsgVote"] } ``` + There is also a special wildcard `"*"` value which allows any type of message to be executed by the interchain account. This must be the only value in the `allow_messages` array. ``` diff --git a/docs/assets/ica/ica-v6.png b/docs/assets/ica/ica-v6.png new file mode 100644 index 0000000000000000000000000000000000000000..0ffa707c9812cfe728681dd6c5bd8f288216b53a GIT binary patch literal 163580 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 Lt8i2_F8RWS zM5LkSv2%2;zHZkJ?6s;58d~n=Z7tmuNJL=c%bqvC%@l*gj*ow!Gw7hV`@Z(ra`UdU?S`L&!RSmn?&BQ4`@q?_Vwo%) zXRWu7yB!P(&h+!w-RBqX-gm|yJ?B2x!|v;MoY7&gM;$)nc-`@LKck_H)8X(rC*WC# z19h9zYv1YL_aS8I9Jw939rSn3+zU6{&n_{yDaS&OJ?+dDXR%M{IR|q5oy<{VJLl_v z%!fvijsq2}^DFzG=lB{UnE%ErzV361tsIJeovpt9&eHSOoV91KxbOGp+=s)?tv4>- z4|2vZZwk1bE{D%LbiZ~^@cGY2&$`=t9)3P~!C8Olru%aEk+aU;n|=;qS*P#O3+zKY zgHOh3&KEymN~CPE^IAOQ&FPa?at$e)CpZ3FW-Fk>GQ|Ij^FrVPRN5$8S%FLAUtcga>WWl=1CkMAc(08nQu>#KcNt2yXCw!bQz8&=Y`$K;m>K^O#@uQ9vt>`R6CoEkWs=vReUd0)= zgmwF}v$K5uT<4~LfV_vmmfOj?yrYYhrFF%_a1Q$-12kQUAy!9JQt&< zD5`e1JH_?Vpw4~17d_A#WDM z2^3$Hetr3@I}w#=M0wT`$vL!XgQdxw#v+v~>GmuT?QCi0ahgzXGV*8(6Y)^fw8yNq z@<0a@)v2d?5|EwM%tyt;T7$=2i;#asn^ho|h7_h!LkZqk0rTk5qiyEQnRevJ5vyCbuFZ$#ta$O_Zqqi; zkuh$6j~pn1pnd<^K@!EIJ^vj)ggdvb3i9;Cry5+U=qlfoJWnNu@sI=f9Y zcRG>ko}?(@AlM=zyLDk2x=kcjl-^Bn3Dks_eP!hO znKD^}d@v+Qeqdsvot3qw7g$-ULz z5rHn2Bde`hy2QG9U$^gz=W}~0457ZO8Zul&BdVlI)1a9(K=z)2ZgoWOo)!oBdY3up z@6gk}ESTM``Fftakd>mbYPQrV+=iL4NfmCrXM!~$+qf-aFL^i?PNfWGk z$zs-O++<6ZGL_{bLVgHYFWU8X)Z*PlSFIx5wkx%BmtmXQhQ8>A?pFj|tV-R&>u`-SQ}(MHu6s!l?8PbJTlMD4ubU z#&hZ;p<*xf7>LU8tD`-`<|FP)zW?4&mMBTPk61-kG=+9;dqD%Xbs0!VQ(fF zks?QpWLSj##=*kmP1T~ZO> zMjQn-?GeRk3aur|MqjLHCrKG2Ey{2zrV?7u<)#=xtw(5Bd=yn#Ln26afkJXM3kqS) zCy_T>hI!=Y?=h7$5786mXU**Z<3i)|_w_@;J zDoY4@7fK~D>wA}bS`a5_n;aiP`-PwmT!d42564793gUH+7i9bEJZ<>7{)<-N&Ye4^ zz6lE!EU-3h+L*Vuw@sfu-E|n$-C09Ug2%D=D^iXAak)c{8Z~8$QDl-uh^j1n#@}q6 z(ARIt<8#)xNVY6Sq}C>*Z$70~Dii5MC#VVY1~jUJ?DGfc`4%13!3D;i!|diuN8tn( z7a?#6+2@h#3juuILq_G|Z#UaWw=^zU$Si$!OPV{MHOQac)jNUcl<`nR7ej{4LDn@# zkEpKi7%8iXknR4D9$S<+?^urVmCz6A+*D2F@0TT=w0gcAb|YsI>&~&V0j5kwri*2; zq~tY2^>8^7#dn#8l5-vEMI_xhfSR%|a5_W|u?BsaKSdJDkv5gBL=UL8uzIRQwu?SO zRf-q;-BGxY?p7z|w#D+<2CnBJOkri1vwBHX0{qkYm#|MPq;H+V`E6@Z2*C=B(boAQ1Ht})#d<_odx3`baZCsHBK<~4kl3MZ#5C=KZ@wh<$@4YaNr`?&@g z(vdKlXYJTyxkF&kz+k?I0bG~)dj=^>Rvza@5r`t7?`mt`3nRZd`IV~O(D1>4UPmLl zFPM)Iby3UKM=4E2I2Is+k}pm)J9*vL@`DpqgCR=SG7es~aun3B<2FjG z4BH+jVzrO8s|F+2R8YReD2;R|%GWhyv}s&AA7}@*-*%sez2!a+e+XZJe{}^UOI3f6 zKYxDPv}uzyZQ9hbX3gsMRq!|nI+h{`+V)KwLPTQ~|H2|hixoDtH*b#r%9?3ukKGG^ zNW#~OAgc=yiMY@-jBEu@qp@Q_eRG zozw6~?*P4q$ZxRKGcZ)d8Z^mjP|cqsujhS={V-Z9tzs_IbxHR^+`3=ERL><(RXG zI(}4b1?#DA%uhr`25=75Hh#l8_fpiH?|dJ0W_#=fk*X-#{}b!p1D&nrZE_SsxN)NQ zzJ|?lXvYq>L$u~CQ1h)Bn(Lp;H{4lxYwqA%HqqDg+t3eNM(Ah^zU6r{So)Yz-Nr8| zC`c-X5V!`KeFn-oMHiGJq?DGPq}*J?U>r+Cpkr3GpO0ntIgTOmspzRDU567P zV17mjwU!gWdf zqE53=Rxw$lnw($ssS{|b(^#L8hBgPqI+#?Z>yLl4Iw+JPusUfhK>5|dag5LU>;&Z! z?XB3BMBVxh_f8$b%d?Jpe-86#!@ZtPv7Cy^OET9#6SnSwU%`V%uHU{HY)5y~)50CE z=?C}d43T8@WI>j2CkSrzU^!2kh5Z7 zyG^ur7+_crd34I!)&ap-Vvh+n(Mh^pu7J6T@50UZIuUQ$+mkPi~WE$@P+9+Zg5or7zdbAv}KNb3D z4-sU6ClxcMvUSK;>4@r_XD<$k8kl~;95nVY2Vb0o#2hCA2E6(hXiS%5f=(33q}1MB zL`y{Fs_cNZ=)86q;Aa7>){qw51lg(~i6;HoiD-K;dUD2jJX55jx>GfYC-sMp~c?Bat$Dj&m%gS4B?JRj-k*8wBNVQK(>LLyQCT>Hz?!>s4d7 z`s^?Es9^w8YVw&FJunI#ul_vgamsG6hQratUC`yOzT&*n!y@oF56C7Gl3L}~Arf3fO_*6a*4 zX=gwZK0|N`p%)!md>nx2Y^M~N?xd6mMHq;4@dHh4RP( z{7jhl%^r3fb+QzD!Y8l=&BW->5igb%m+^>Do=QDHdl-Sl@xKs5^8*^f zWMFPf3B?wMaxG>2xYh%~GYI2Th|MSr^KTG{`Y{f}u&+u3H|5?hJAK|Rojz^P&zuPD z+>8OM&eO4pq8tIWzk{%jjZ!#|q5dIstM4WRO-qu@zNC?vhf;1O=uZan=tB^u5^(hp z*5hG__>6gHivNy#jmn9ge%a3k7R*LEP6Wcaq(5Om@4j)zMfMvrPakrN)?B&iZeJu% zF562gl!GB&1|;eX&W`RuehhtufCEqtD-p`I(^5x*gujf@UoBk-?lQkFa zS&>ws-KeLM-M=(h6e)wJyw0aN7L z3y>MecHNb#i(#y>kvg{Q=z=ro%0%eVJjk{q7}}B18A@MOH)1bpyYe8LMH70G2<}## ziu#J+A{$R~u1|?T>zt~Y=!l^&x^5rj> z6>H!PYX5#EvfUbx=00*n29TCCn{3dP9vp zXS3$T(LJJNSB>JkWDw*cJy!C6IJUr7oX4$w!$FdukH>i~0Un7}urbck(gUa56k7M9 z---vhV-RHp+_K*uht_evIPGv};lnSFo^h<2*q6(qj52Ut6+Sq{reDva$f}W zGtd|z&Y}k`fUsE)^`^sI3LqfAA(YvHxx^)}u^%W#9|X6MpkF|_Ga;}tJ_)eSz5807 zJo#<1uprOS`tQgFCiS3vk_fkpgvvLbFr+siB%2qzfplldP=@#MBv6 zDNLfWQ#d*bLRd(aQjCN|eTJ}(44U&bD4XbNrBtSYa+PPF+#G8U(TQWEj5Wfch|E2j zLIHAKY8ym{JCu7MK5{vfQ*iEDmoH$^Sd(vOU^W=2V`@tB-%>aW0X3eLjAU8&9X3|2H4tWYW{V8&6GZEli zpyavHRf>SGKzH?lltG66X!Kn-K$!^0)4qUTtuUBVaV(APq=7z0a^AuK4#t2MsfD89 z>LVk08pQuIfJ|v|A_^c^VbimT$jhEM9&!fe0LR7e{6jOmJ z$B~CM!gT6dK$RfmR0TjL&2ts=*0EsNv++0w4M{hO#XQyTs1H$;D9Blj)#zrS`&l`j z(t>)U%amTU5zwqP=e!6wv*hMIH<~Qj{3EiyBPmu%h|IwFXRr;rk!3knPk1fT4%Ir& z#|cQtadV>+*O3b4%je};15srQU@JlD?na(J<2zM?bSp(!hH*a{5QR~6doNi8lJT-L zZm|YuS*wQJt8=7`S)d=08;>S%FXPRAa6;X(2ivCDlM4q!=lX&IdyB5*&0gp5+5P~FluiSPHoT?dVMfEY*;$ZYC*HTf3=pYB1Ks%}kX+}W&K%^cJHGeVpiyFq1CSX&Boyf1wH)i5{{v#l3K>I#H(N5Y>MKuu&& zRwvA<{sTiX9(#jS3+0qIcscG)@0VNRB`L@^AxKRC$jbH*v=}^iFHBmZWighm%BEJ5+}B5 zu_Ic^(q(M%krS+UG%Jb_U$u0xRUGgGN-44xj_|~;kqw&-fg0$2(lY1BY281lXszql zwJPP`v%1lr+tY}VZ7)%UgeVv7ukiSO@^r=S6qS2d5~U~v>OAXw45grTKF@lu#0mHW zr6OUk``HO)p#r>VD4smr<5HkODq!A#Q_&cQ;vK#NbBHL2ElRZwYNVs4I!U-cBXRC} zqU>^}Nong*Zb20BeTrgJbYKLZb>TA=wW%mj=ut|}qkPo?DBv&nPKX)92c^}7IcSdO ziI5a0Oq~Izoj_{|qJ9)0#h>VKDT;h$xvE*DQhLM^ID~#ZBEpbC{2dXA zI;0roB5JB}5IHI~xOb3e*GN5WfP7W-*^fwsSbZK~I4e?{i07*HQtG8-;?Tm`T;%>o z(pCooq&#A~K_Kl-k+tffqt{dUOij!-qI+u~8zXZ)JDKN6fT2d{PR(-)h<^QS^d7>{ z9#2YZC6ImKin+AeV2Ui{HbD7nw#YoFj?3SwtZ^5;+K_2z(mA+(%qn z3Yv@#RvIsn0!8!XEKOq0rI?Gd2%t81Q*YJGa|8SMb6kN&r_dE=&>5|W@E>4Zj#JeA zIcvBV-F<{P*JEuoMxG?Pf$k{4pXB`$);%V=DiGbR9!s47A#Xl@W|IJ!;sM}!oE>-P zH~qn5uA!GzpYq2M?kxOqMZ#adbt|B7=`nH>R}!X87d^7;C_D#UP>4~D((-@=7sMD9 z!cvOD+*}CWb>uldKu}apk-)^Dq0s=RLC;huyM;P~`UtfN80wYTN8OrL=kP0$gfrfV6KTQY>uub?M!aFjm4FcreE9f8@NG?p2l9qAF;r4ef1Q!rf% z2S&!U@+?mizHCic_ZR{vRi$e7!-Po|`Kq@St6j(ZP9AZai3Kp<*;{v7%Wgf)Kju3o zTDd~bLmM|^7WEOCtV+_jwsFgLdyqV>JuF<-9!=?QgMOK0r33ERz>aOLZSQX_>Lo90 z)}p03hxXc(Ej#S;jhj~VEGR;UK6YmQ3~Sz{o28gA%N|Yo-o9_&+D1-ZVDt7JvBk4z z+1eG$-D_3ue{>5KCfQP@vWiLKS`f{dYJc*nRZI!ug1}#T+Q*8dOm10`_w8DL_ey2kE!E$NT^;cBkJy~;^w0$5*58`zYI7pBT+_T9TDr&I__OZ z6wFn=8z;Ee`MLI#I3Wt(zwQw;W@Jm6GPDK-&*pWXKKy*)EASVtK&VsTJP)yl>#Cab zBs2}sO?3YYXZ7!%S~Y3s&YXoPBDz&TUkH&`qf6!dE6O*hX0nSiO zS0T1f1^_iw-JQ`fS&@%pK?SL^qmVgX1WgE2$@jJ%KUX0oCfEE7d&o7}14I z7~!6Jzwa^n-@~~P05=)OA~V;iTCb4+Xmz>9WH=ofA{04*QQnkD!Z?mwf%7O;YCP-F z2GB1SI&dQTp#s#^icAk@n~Iz}FFIah22M;?bVV$jERlYwSUU%LMFrkI08=fI@%M0o zwqXynBN8JWzlP^ZY1JBSWu3bM7-@{<*dp&xfwoev#6}f^D2N(zZs1zGP(vcX=4bT$ zXRLdcFsIDj`E7Uz{RuHsbjJ%31<%yOodwTIg#Ul3D-itP`D^Rotgxdx_6jXH6a35# zp~l65@Ftf&J}7@hPzJFzq$c!QnjiWxLWlbiK0Mo$7EQem+AI*8JB1>w^!ASstXcf2q-b0tn-U7Izv&!#T1rj44~v2jDJd6Oo# z$A{hy`SaPP>ErDFrAwA3z}E^CD(ucZN9vRofggCuDpf3h)emi8!;dXS`36?#d61P! zmDE=3*keVqWUzy$ye%0u6JIRfXt5qXurl$Z+g^?^w`y66vtv=v-L{bAnQZ3r zP1bwd2&-Ngin`USY{`n1mNiReTd{GI(TvPJZ^8gSyH97=Uf+J}zK$O|4#jwn%&uH; zdsytH)8?)N2W|Q4HP&k40(<0t&t@JtY>(q5vWQULsc>27&w3Pzgt-cq^McF^NzL<3<7B=8IPvlBG;*AEHMjYfpe~HwU#=D&$!7Q*(eQMWSaR`?_H8YRo6aG#m1) z5Yg<3=sOw7C8(oQKc9GzIeY*zuOUj(oybVhYSZ}Ano-9ma6{mYfj8hZ_Y*J?|eWS z5qN0O@Y9MZRg}Lar2hPd{Q8i&jRkO%Ga~>^L&o|RWPUef>{6VAB(Mw>AVM&f>uJcm zr0*3?&cT004yF;ENQv`N4xON}kk176s95_R>lPcE;9bsnpX=Med|FXgsC40M-s9Hi_d<@bq`jLDP3fcr~17CBpx|)D_58D4xZQAI8=DiY*s?tyO5W z2^64gCW?L$3TH9A0}*&%kyf+};Yx{~)xuC0qtF}##W!JS!4y!9L@{F6`#Dn6jD=<^ z7yWEl;T(1b!%_vxAA|ObhHWPMlmw9wvfQ80ZXbk;3a8Y7Ee4KCDbbQ5)Rn(4RwXrm z`vrrv9m3`asEQ~{Rh+yM;V#;^k3dC4;W>`zM1Ls(LYMp5CL{F)Iif|d%7yb=f;vsC zZ@mxgZh_oZvSdkf5+=6t4I0^b-$0vNrm%g67Ur{Rq0L^k-VPl-YH>*Y$&xL*%|CU{2K+SEs#U4% zes7A^`_wvg>}bC(T4b3@l(y#e>sYUat1M>fbXJrt0+OV%?dz6VVH)n7eDKun1q6UT z#dp~_))FlcswF3@0C~41LG86hqGcTb!c_DdXEOszR*vap(gxz; z#L!UIU6-fVXL~cYot*=<~WzoeYM#}^w^44h>6vRuW(89>%mX#6ueQgow=^sgs9Cms5- zEY6NdD$c^(*O|JnnaoM7e{ya#0&r-SQm4=rpiODKH<(v5wi}PWRlYfV+T5DAc>&7d~s|Ux+;YtDrIuYBjb* z>law|9v$2{@+GWo^JZ-0*tQ_3VGkd-$y;|? zi!AA_c1I)G2Uw>P`K(jE0``8) zNLG5pk9IgDvOSNV(7w)|+2!4?z5A5y^%`RZvSo%PI|Rei+Fg5W?cn}) zF?9yge;&Kd&2r)>l@IHsFdOu#0U_-8gwvJQrM%FpD43HtR4Oq4n#hlwumj|liv2k2<#_zL`AR=^1%vKjVU_`mG;CT**FgT?#LSw5o9-2o*uM$-pC)YLfu zEBYX}z6AVAf*!hr-r0*x6m9e_^xYNoU3>K1ZuHH5(D`l{>Pl?bg=)CKsp)V7lV?P{)T11`IWOOHMa~K`dn|?cLfV!3RWUXaN z)@BpyTM!2-A8WE6rX}@QP{-}tq|#0z>a5{$7Tgza{DXcp5iC}bdKNKeoY2|6;NgTj z3vc`le2XU-`rUi=7r+<#5s1mPM+hr~gIEb#ArP*T(xcRqRtOzUT72K&hatJIZlSAf!5B zw2B&Z9>Po9{mSl-~h}AfgVyYi<|TL1BPV_jNjxKw@VSc zDuk-QT!KI@MuNnw?AF%eb#HC84jp$fK${ie|4Ti3GyHAEhvEJp2+D44(azzVU zeHxf~J$OuHBEX8rj!IfmLOW5glx-+l&WdnthjA1Rz%KN0nNqMx#Bfu7t|d$cCv_$e ztI#6gR%MEl%Wvi#BY{n9)6~uruB|g@xOF^t#Ol_n>8@#uFPhrKFPqwxXmQM+dnAV| zBgjlelz>pKxu-5z4suY_u+Ga_|3H*SOHzk4=Os7}y$fe|3!OKx{`EnX#CS26_r)3h zAwpgXLJr!e`iw}3Xlm7A^BC9V!*@eYq{iYim#r+wQ)=>MM@!tad~%))CrnghL=Et&K!P6j{(QW{_@H1B59#O4pYN)n8$-{}tzS7c%@I zd7OTA_`6SY;uR!u`y9K4@%XF!JE1_b+*?UhC~0${EN z`Z5ReyujzH(2+gRm!e2jL2@@znMAF60br*!H4_@r(*x0;O*pn9#lOI^tU#1MOH0}AteDmuO_QR%#iLrF50<1F;$*m4jy6TJyR7pdk!L>x)b@6y@( zae|I;-tMF|DRQG!Rk25Dxanl>v$xVvA z2qg}{xED|KZiJ2(!uSgqCsrb?-$4N6B6UR!IGb@cpAVNMQbVhNG_Yp{WK%h5*1Vu(dRz~>iv%Vl4Z7H%%KtANDz(hm#*0_ z6DL`giXYhBXff?7iX$1uz6zCNa?Q%SjZ7a232wK+4w0G9XqNUIAv~I*;|@e+)RSQm z%A*=PEfe-aF{;h+Efp*Mvf!V3W>iBC_)vc6(W6X z-A#WR!RxB25}EH)oKBn#ISR_FRXxHlIAU#xQ0yYiUIgWroM@CH4d?i7RAHWC+sev3 z3Q;Gaq5cbNZ~!&_CTiW6ujGN->5lt%pP29Uu)@w)a;ETuhOfYzx&ooUw20B7Tf91x z=-})@MqkG-@a3;b1dQ9_AlC6?M7Q-A!z!GuixEB*!!|pqu*K0AGN#pac>t*U9c0lc zQXu<~W+}Nl0h!f-sI|HYYb;{F{5S_s(Thr%%!}+)(=v5`4nc1!5_*YasFy-cjNxs_ z*RwbdO+fBfBKLxjzhYHV!^>I7yS5x#HGXP(x&_cg{dFz^l88x8V=OvOBo*rDd=nWz z9A~2w^K_#P%(DeXzdv$(J)~g?taMimVoJS z7jyST-q%3K@8SJq9H$KR6Do8Tqfuu$Tda}fy&A2l9^(w3Jw)HAiKq154vry5LE~qd z;;A@0AG%lLuZS?aC;m?!q)2u1v;&~YzTdYoAsk-g=`>Bhzisc zTI~T9Y#X8|*I5hor4V(f>L>OQT~QAKu|=uz&RHCU`Y=Q(@AL?tr6(V?7zVL&XBW~f zIUU!Lg7>Z>q;qk;4EznDA4MhBDK<)K5XOBUq8K+w>$-R!toH+3SFVBH zUkqi8_7%vzNPt*1!U~mV5rJ)mF^k}{UdYVPMUKk=ROI#oaxXblpQDhzy^hRhVaM&O*{wbfCAe;NsNMijSO*8=96D_oxyA}-VZI3;kWxrhiVp{AtXy<7-pasLGQ!otQEPC2{Ay`O8zLq zwl;>cikl`PV7@}|io^K;hPE$hGGEeBcqu4C1-cBUARKv-?M1!$83y1bgz8y@NK*ut z5SRfN-Hmy!nup1t8ZwwGV3aE#(}(@4fKUuaaDRXR%|+y(A8AM}IF5257jup$ti(q+ z5eE=jYJ{WimdZPwN3@_2LM9JkV)YX6L#V%lKs<$DOU>s6adc$xw?%+SIBNL7_H=~L zi-C^+q9!~Dt$i5(k>NTLLsW zj*Xh=T;ZDM;taIJ@eq2mlh5LEy~UZE#xjJj3d=GO$xxb6VvO-z7|~+6DC$OU;#yz& zAE=Y3Sc=pcRLD<393D9yhj9c{&@5#C1Lmbvtkb-w+}jp7ap%~61B_MGa3Z9X`r~*t zWX)2uhM~O_LK~}{VEc8fV^fruoPaqv3M#l$F9of2JW>(Fob(F<{Y#t)HT5}9+LMak zlmn|~K`U@JT9TTkT-)mbfo@@PR-B$VC_AxkR0pM!f{DoSOS8`E1)-=$P3CqRR*i$q zRViv3%CFtdF^jUl>LN5=r>clVR3{pBC*dP}1^%a3;7>T>-?sIL87-PkM^_1?`GRyC z0ZRhIK1R05XqyXQCH<0s)Ss@TS@vfe0ZxP2r#3Px8;H7w^pYB;#o~Co0l=gOg}^J{ zU-|U+IObIdBGlKWJbFx^(FB^VeGABQlI<5EAG=ZG_XI#>D8_tKz^wGhH-S*g0b_D7 z2c_exgR>0GB>-kBG;W9<)R;qLxdnP49=c^LMa;$6Hw%%GADDj+=B(Z~8UosqG5=x! zZ3EC9^^oaN&@r=+)y;8yq_2jNlJq&wKzgFM!_YOgs6!Chgrdpgh*nerAkuZ{dNZ+& zI$0m)`ZuFn>M{pK0j8rP#B?a8y^%OB>Jy<4 z;m*R_as%Izd3qd!H6Wb6*C{m9@VpuN>ZL!*3062%P1mNuY}1O|a?z@(mHb-LLDXSV zV-z`yRY9uNknI9L`qdcLO%Pa1$z7KbpO1b4>gYQY^g_mU1O(*nYrbxwmG&14QbP=X z(QVGe0M%uE%IBrzS4cFvd@pC5LHDudT8a87e9+RROI~Q5!&4m z)>oOw5kG(Xh*0ov2t7qyR+83}DP~kFNMC`m%%>_sPr0gJV}SR{6{-Z(aXA6$L>0N_ zJ4724y%76`@-J6izYQ}HU9ySTs0*@KCsdrRP`D8ND6D-6g!CH1yy+13WpHX#vmyGp zco^J!5SkI#PZ0^#V;n$m-$XdyCsI?5dH{8-*0pTCddr$|pKjnx$QUn&&{Znbbd<__ zJkNs@qcu;C1NZ?(yOi2&YCyg~aH~n0=H;d$5gr#Bb`xi$A~gh)2*-cKf2D}2m%|E_ zLNAKV)$wyG_rDjYlE%5a_vt!HN)*mtN*eo2LHVe7?LO-f0xI*=xrZL#Vm-NYX(|>2sz`8 z%oMPqhM%hdLLwk@@+V7Vy^!fj2~recB*u7AWR_&D#yRBCb_$=K`tv-1zhLBi9pso0^&uRiFM2^ygr70sdjqT~C3gtT$OPWx#yHoQh7*ts!+#&g zuZ#>9JJ1%4YNZRc3S%UigF#<_xukTNlWw46h`OUI6mb@iq^P^biCeH8aeWm4T_$qe z_t3-XxDKUK)klw}222`-qf!O1NtD`?(6QNZl1k$g2_*alQ0HBAng~wL0bU7U%F6yq zon45${u+lsQTkumZxD_`2qJP2Oj13$PIW5(iTMm9^_Y@Bmu;yC*Hr>wO!{I9bEris zm2}@WoT6HsGcjw|4_iPHbm@y@fQ~BMPJ>>E&UVsoN_o;yWaX_HVRl;3{+0Il%+Anh zzhJw){lBWW=1%-?O*BsYNPp|#{EFdKnMe6BCcE#429zImR#I}*SZfW2Vdt=32oor_ z%0p_1x=Q*HavdQ98^Li9VW$@MO><{9|K|~HonH{))8v*B0k{3uefzRNC=eyRp7}TYZ6F2FTM+7zpke(O!*b!3J8ld43OE2KDH=W!9Adqg z%MBXqcs<4VPM+9p9CL`2m0M0E`p=%BfH^5 z0AYvm!HH-|$SW0!e~wy)C@8lor1i|b=u3)K63c}nu$S!<#D?JrrKesPJMIP9HEIH$ zfW+v0qQ6s@?71*UG|r1ahqhwb4H^ZZ5DIb+ci;f1yK_m-v6APa2vh;H!qGEO0`+l7 zgfi^|{W^uS_&FhIZxrM<91^7nEhDWcAN3WY@q2=@m~`x%eT*Zi=5D(GZP-2@PSSqn zwd>A3%So+9`e>10-ME25h+@h*y@ylr367Xj&Sv36=E6x*5&LS+Srx^ng60c2en$wy z=j1z+j?b0Or9K)OubUI;zrap%*Dd@ZdQFs9yIk@86m2Ki?-Bk$|k| zD@9??0$3C$0-XYREn+!8{Cfy$2C!)jW*kJozxjOCr-X<{&%8 z26!5lATb20m%>2|=`H+;Har^6#XPo&&b|fMM~%&{alQ|b-6J{2XUr*Wm@$2p>sxW{ zt}A0VBVW@X?+eoxXe{S1kABEOwt)H)Wg^|REc#Cw07|Q^MWk3mOnIp}PezWXv{f1M ziYBXWYzfq>{VDd=m`sXs3$hKO;ne&9@K&F?KGB^@CwxhvbsY4Yz&AAst%h!r6Ed3f zegHtHHCT(hFTy%2S}~LezkoGGcFr*W&3sk>9Vfjc2O}dOpxB=@+;lJgE9LpkhjBs; zwdeP)+VenPcm6LvgtH;NxEkTlU)l;duhF4a8q#hIL-jsLk8vneeF~_Cx?RQsZCHiz z7aQ213h0SKi333r`eL}>1_hDuZ9$=LkLdw6ZSyt@jvUppA#i{2MnF;wb-{w%4wt83 zjmS-Y>+C=sdi_p+R z2SJj4vnLTB*c1%#2dsxew&y{NW|9h(mh<~Ye(0v>sNgLVLhqX2J1+7whfr&IkfOck zpVMjindQJZK7ryHNv%a23cMEkoFT9FrgdTs#_#t-U;hT>Y-BsmTz3)VYvWY9yP(HC052{|LACWt@U?tg{c>u47(_IPU^pk3%Vxr2*Ya_Bne$ z(1IfpHXi?z^{e^5?YSRpk5H=FInPnuiyWcCf-JU}+A3nlY;D2ugWQt*sJ$+&U3qH5_jaFL37@*)n zMiqK~%SP;2vCIeh?;y!%4CUMy&bI(W)MKVG(T2%52l7JJ zk85D)YivUY#KD;mQlFP1-aN=tFVbO`A`@GYA{!5*djmj@=F=R#CI-4q0Aeago<$l_ zNo2N&Jv1&8;TTO+U^GllUPRH=*XR>Kvx#h%4aZ0g+Lj`>pKuH709ftf94BysdU9Ni zMXKT8LP|pXu@ya@99>@;07t1mO4DktdI$if;i{u!{)suJs99JZF{r6q8uX#E z0#t;pp(b}KG56F~x^KR*523%I2$6~SMEpnB;2+<4V=p9pVsFz5{6;#28EDB<#NsCZ8ijk@UF?CzkG=_sPv<6*PZlhR*zAKi?P9E52 zV}AabO0a|0Hdh+UH+Qk+E>hGUfXKJU5Wenp+}eLu&svSWWgE9{vsxciu@w}bjhr#t z8rP|1GeFyCE?Qz;8-Hp&XWX?7>o!~a&+FKzMf6yhzuX$sscuVf5GG-yz5^=VC{t=X zf&%+x%69AWRV7=nKZ5;0xU(6kzo_2kp15qi%jL7_yMk?+_jMauI*-jhyvve z%DyFCX9>dT8r`Pb6JpKUd8Q37pAS_1lAQ;^S2LP#cYE2AS|x1N+6{1W``GI0C9LQ# zGwmacUFEF~TyV?+xj}_7a3dl(%g#GybzuWa3hFa{`7sx9%7HS948p(sXrOh+K>r?u z|JVg@JB5&}g0Q^A{*^Zzv4you*xXfXxQ1Ic6Tv@X$qI|eVyC74BR;}*ILa&{NZ)>t zpX>2MC2}~HSZ`-G7 z6We(lnfBaUHU9Y!2W>%Mkgf8%Y9HoEV_rhDysz2EJYR7r$j%e7sEOk6@&!FQeZ^{n zM8%+wL&qa0ts$vsi9?7eT=chK=9x53Z0pvgi!C~O%F1QVZTXn&j7xjE>j{8r}$58;=O;aE_}cm3jCZ zfs^q8a#PU?8PNU^PP7R#puLD3sBkz6x-1UoDG8b`uw)Sp-RkpPwRB!M2bXXjnxmtx z;M~k0!rq0sY(fW!`qk52Sq{w@QjSIy$!XWnWy?q@?FO)=255?GsL#PJ=6Hyx^#JN8 zgwU&wupj*nR1mEOtk-caKEpxTK{0k5uBjYozs7iKII1A;;{o`nuS8yqd^rUoGkJ_$ zu8HFz05J;SPf1c#l}fc2IWATurP|2R5($aYxi(_-C&BoyLzzV zZ_;&D*KiZZE(5Y(>8^Wljy%SXnEO-#;3?wP{NDKChNu7W_Y>|cyzzJL?VMm#(m7Oc z>5f+j?1-8yKf*0VqD1bGe;3RNS@@xs!kE2+5L$>3>V-kAF%h((6a~1-hwQlbw3`+r z;c)y}B0c`e|tZ=5KcpDx1C=>^7_U79@ZB zSMA-7&sEBFoP@q~|(^ z5LW%MW@Q)g+0S|7M2~NuP%ofD-?JRgH)ep%&z+Z=fDn5M;@<@!8i4UU4^-&PfVOVB z(DP^sY=7m7E(dp^6zMH1(S#Hzrvt3pyJ@o8evZ=)M5!o$Q8BJ(f@E##THIw#+JGXf zo;9x<{`DfA=L+bVcdM4R7iDo@Xd3r25!b6O&XYfjZSP4*VW`g~db1U!B4&)4D7JM( zg0zO0nPY7fsVGiYu}=+*`{K~#ji$H78Wcw*jCnbbXE{bBlx|tlrY>+E6%%HP70p(# zel4dnOHr+U1sAmge}lfF>VZ2v4t?OG`lx@kDmiBP0OKC02# z6u_*UidLXc<%AqS7Wcq7R|<`K799lrPX*v22cZcmJtBKphQ6wZPR&KSkRN(YAWLha z%!)jWLH4%6m_LaF5lHP_^DtUf6%o%+)Em958o}>D@rM`5?oySjp=vuKB0lJgd0fLV zwo!V|D2~;K2#JVA)ZBCz2DzG}?&aF#Ak|`RZYv*F5Z_kCKtJMH$Ia>!42 zUoY{w?$A=+eg5?UFX!yN0QdQEzx&Ql=WjTE9B$t&e`mk1zx%#F37#WQTy*&7j^XET zItTq9y3dzgzU^#dTk_bQ0bW;~YmY-5on^r3E6#1`e>5gu^mVr00lQ~xz5BpfbkpAn zedRbmv(M>(VE41rK~J1h4<0*5`TNoPs&kNibnZbM>#pbIY`O1FfV1?<9p^;gBj=~H z*WGq(okchNougsr-+ep4`PJv9 z`#1-F6;^gg6%iA39UcU2|wq`i-50O;K|hJG zpT#RaQ~2S-SKv)sflvVw68zBd8(-OZ9(+G^f^XRm!Xq!PU%&3uu2F+)_|5qhy@e6$ zY`S?DIex`?O49{;DLCy8pLHPd`29vqJ$lZe$(ghG{55A4e;TLm1)@{@9oY9A*o~ag zUYDKi$TE#(mv1=}I5rfh4s_zqY;>ds&E1@?2fZBt5BIfi&?DyuTE&YX+SCN^x1m~dHBrLJ^hZIcaAWZJvaTFp~rpPZ9ET?ndkMuAm<6Utn2QR z?!G7eA2@y44iXGU$8NFrv=hiNp%!=AqpzVkb$37E7vOYZ4uC=q(Gq8r_hpB|Z1=N? zCoejy!>)m-iUT{J`#xkCPV4<%4k^e^z@x{`_dM6|MNjl(o?cV^qj(6*#}NqyF;) zDLi@)1iu1#mm#FQjH4Ey6Uc)Y7j}XW_<$U%A(~H4A+3z)B~LXFZ{Ks}i4rSD=V4 z2fXaVB%w{o6fTYhDpUq0qz!~ieK>C6SW!%5b|WaR$`T_oY(g<;igVvRR`BD;G>y4x zxj0AGxH0S-&i!%nc$OgS*SVMZ?`3{@-iiMIv3DNeRaM&-p433-0YVKW^xk`KBE700 z(witKA|0eEs3@Rx5Jjp;mEL>rEunmW}j3 zP6y=VG_<*8J88O>6l%0wTz>}<{WbJ(c#rdm&0+rWv(CJWFo%akOvVwJiA|ItJ~6Aq zSFTxd3Q;nH&7Vj%qZrqT8!fVR*mQu-)imA-R=4bH=K1h}<)Bblc+}-E9PPYx#i9eG zucY@Au~BWwmWx-w+Il(JniZ_8JaUcC|Gr`V6_X_ZvP5IGLnkepM`X($Cx+7?Xf@HW z_k0pL^(f=13rGacsYrrY_8Zxss6?%jvgS3srxdjaVxtn<)z8T2UGBMXufY!<*jr>F zgezS`^hdQC!la7AbvYTMbVP>Kf1wJ6(TYkbT|b`8P(88;Vhy>>`wEkqS9E9}k))W+ zSIvv|kcl}614m0>0RG(1{S>*kqt@go2wYA6EkZt^`!r{OH)Lo>&N7 zHF{G#{(`2)WFI?9#-m}1k@_?bw32C}C2i0DV=r!v_EF~%vwDF}>c zQg;FmO38UjkdYQF=_r|jx)e9B;<4UWpXj=}E32lf3z-9DD}s6bC-woc7%3y&o_c|s z?2ilBlhpaSH1HgeKlCD^-wYk7=sXSyBo(3cB8!0twIw4EJ|?o4=BDU5Ia%M)yANA; zAV$ILH6pBN$@K-1>cx9{WlQa3L;CJLPS@omFyqCx4kX2OT93s5fMpmRXYtXvBq0XQOP%0H(ci(-~q32dZyK!Vys*n}Vj4iO2wP_z{ z2lQ0g^^^2>D26=muy2gueLchAPh;8piZFj)j#0lRFETd8crLncAq;8(Yr4kVzDEWc zDwKX@hBgpJ%xophBW=FWX@|Y0C;y2(Ep!s@@BeBEpg$r6HmVe52E|%QFeHm&5MIJK)|a~yMr#m{mw`opC+v82aTYIgY?`zE zh@$ibPE!dizY1Qj-@b3}(d_Lz@QO;m=11`kB=uVts7yAD&v_V_T6%rb#5+(xSD9NW z@a!p3j`NvgW^kQlfWe5?^&Xg(IN(YBNYkc8$(Er;;!DokoNI|TR72_5O(?NcNw8>Ji{hNG;^<1DG+B+RoAN^ur-9=;UY zrRO>WDI9By;(i<@Ds+5zC|r*2z^nuX+KjTk3XU_97>rQ%NhvNNGb8$Z< z4>jAX#b-nB5v5=j-s8n*XD=I6OkVQ=pQ@UL6&%-*4fG1m=p5$#KAWFX{_5~4AX2FM zbuRtFe?5RnOF!;#CGh%5AoQCSIl70%sX50YM-MfkUsdnf(ZOr?fZ4xg>CkWLg;N!M z7$4m-g#D=sdrywALgA@`+fa<`15{INa5aGYkO75I;+{i$>VZB#j;>MwNVNc>?%vK` zP>+2``YZqkp%L_}HwlV-!+y~bQVI1adcY<=m1~uP5ojZN?gP#%$6x`q12NI-f-Y%@ zlDipBj2MoH##<7M_vAPwqPA|21EK3{Ok(fwVxFQ?)gE1y;_9fZXEe^qdpJHS2L1v_ z?E4t}DPStn9$UyOwgbhw$R0EV*1V25L4>?*5_{J>80Koawwt=dTdcPlH3x@*OZ~$C z)O1z*u=dkNtXaK$$c{JxM6nzFge{OG9(xQ#AqZH%HFd zr<_;yBNLHDQs!TX{qr~WdC5nG-)E8YFxK6gc}wnsFAWT1VR9D!D*j?^e;lr}@JETb z&!39~u%{x#Qj+2jx3MC^9vG1;!Lf_=Kt`XUBqR6|D=M&&2>*GESJ9e^`*;>cXF-&( zj-7GhqIJRu*BDOZUo{Xi?nTq4w(#$uNY7(jZ$b$)#_5=ja!ZVHUjYTDj?7sxBuk+{ zl)>1CqIn+$d59>^Q4GkO)C8mh`@f6-snc>Lt}nd%ISg1SEh+b4jM3^SVl|9YPXTp< z*0>I%&m68*A4g#%MOPnVFbATHHlwWH4`YPTX80?GwaF<|D@~L|-KABimIY&8&FKbE zfF+EmoB=UI$N(q3YomyKe1u`E+oY&{2)aQLCq-YDQ8OSTJTGh)Cx~Y08pYWd7SIX% z4#iI@Z2N&oRw?9^2&FEEOiV#iykeW$f>188n|sk8=R}U$a*X(b zI3iiO2U9WZQ*zIWAj3&GR9?vT4X&ZWv7c~SK1nZ@jcB%*dw2<1)y0|G!G;%b>z?Jp zz~9D(mxhf}4dt|%iRGdTc+vpwg}QL76Q>H{<-7_1S}I3ksLQBiRTS2rg z@BxN9viq~zbuRPVevv75&UIk-Ra-jitn*mk3wO8@c-6y8AaMZIS(&{t*~orxP^v`0fveMP{gm3kDR>ok7FxX(^tNKtxGR>u<45qeGa zab<8;l&zS@-cyA4WMl6T5kxS1Tz>YjpU4z^M8GTox>G~*q_;K>LNc3zeol*iP0gCd zP`g)-_vhgG7<6b^GWMb*)mq|FSX>QVdXBOWi2l4T z^wt-&-*a4dvJx?wn=%~gzaSd!`RMeD9HWc?a8jqqXm#{GP$c> z2?+^~ea~Lhn4qKjD$VA+Pmob{9D>aRn0B+bG-rL1?dM#l3y-z-nVkDWGAWOFuSV!^ z=i#gK>o4{W*I9UVZs996)4=N?G_!i~QBIjeid=~AzSs6cc8xv;)#MPMV7^5ObSFx3 zjxY8yA|oF|=_w;>J_cwCjLR$-@k>w$-7u&XLH-d#TRh4Ih@h{>Am2r_yd6qN#{5VW zrx+eq5w*C0f_N9@un>3)Wf@in1)^@3Gf`Tf^8Qm?Pu%@!F@Uq7NYxx?C5EXo7vf>| z!~k9u|iZp!S>&E*ghia9~)&Y1ux}Ql8BB>*WN^L+AvQ=OP1rrT!8VSD9T?3 zv?B4xQJ~rYPLNJj?}Kv8X)EVA6@1qk;;_siDkDcHm$w(a9O!NgEp2`bbt%Tp9Djx5 z18|0F1L0BBU-x1L_o6G2{vaSjbCF>>WRsuytc4|m;ueSI_BS>db$nKXAi;yaXCrFM z=Ax+3W@NhyUhl>j{)+tU=5bNxc$|9_7X!Y0*ycH&C|6DH2UQ0)oyeBx{1oXwfJ1Qv z=OZhdhnlXfPb>v{g$kAD63tdav04O6G}fW}?-EcdLjiAp4Db)h*lGwylYqTKnN6{> z%>xS59!gTdl2)Lv%b}~qu0$;wbgAA>FPkrc5!K{zX7shjEOc*tGV4bO$mA#Zl$O`2 zb#OWt%W)CBcRl;kW0YG#jBXKf>|iewOWq{*p%yq7Vn@>0!EwRp-UbBGX0dlfVQwV| z22m$wY01`#{&hAz5fp?`z-uMUd*@kK6Pz3EuY<@A)Pw0rS>tbcT$Ny2F7)eZvIvvK zB1n)%hWa-Io!X+u6EpAG%t0l_HQ7tnivf_jS-V+@SRWhlNld`HxP><~(C7RGUR zIy?$<*cDh$2NbbC4#9GiVtdXdT%>raTcbeEqDY3JTzc|Yc+TZGCdt9}7vdV{QIg`4 z{uoC?8^AWA(>^#hM~JjB(}Vhtba{#=WQ&rYtF31d|ep8hCwb@U8R z>#B3KpWsuRV;A!g!^1l$uJ1|lzKH_PfKp$DQ5=Z{mmoqVCW+n{w#_l16)oA!_2n1{ zUn(W3l=K#&NTpH67jTdQQK}VDuopS+ZshSHPL+B=2+v=cxou$H;!MaxR7?2QwanoZ zSrdPLssQ0Bs0;}o2;n^IlR1gZduHOO3GP&mOwdsb@WcExVE*el|7kXecTw)TL0{vz zz02k$tm;ALwG25lhYs&1a@s;PsUTSoWnR=>x*{8iaJdsWCNpb^$6Cf6yFhp6qz+x+ zQRXkmQ##h1k@KzPTB6OoDum;l!H-4<>3xj0z62-#~o__qP%hr)SK^fJ> z7{lsHADsxtdX9gS{YiZnhO%E234_$?n#(Y*L-?y8iZbZR_$q5IuC+A0U;P)hla1a= zRzg84)u+v1pQ(-0Fq31ev-f1=IvS@4i0$XP?~#$;$T@=8Z|bq3)g1}CTtI$dyIr*ktkZ0g*so)cfgZr6nXlo%1eK_}P2QIUS% zgdX-|PTHHbKdzyUO#8g{TYvW5ceKYaMk2Rn$^sB<`V9vlAweQv^!P&dIA5G0p#{X8 zbQoE+4tGenPH)Z|7a4cnamYFojFdew6z59y7tuJ^Pe(7%O**P2=XD4)bg5Tkgc)!5 zPaUw3^9Muc9QK-?xX!|>aT{N`S&DK@p0_i?{=A4q$Qh8qxE}+uDvC>EBwl@Pj7cB9 z_+Juj=uB5h6<)2AF^h3n2}5-T?@^>dU5xjmh?RYKi^xArH`E3YA)p{C24DRCP^5Mz z3NL!L5jY*aQF=$o3{1wb>0LUbb3 zYD|`f-UBj3_c51N90PNYtw5Qz4l`Jt`IC-)69ZTZULB4r;`Drmk>3iXpBN>kagi>b zlZj*~sv@51MMPty(59lav;n9tVFPnfWJz!$Z!#&vbzbnN;+*zJ2_7LTuSRDXak3Pl zUr1EE0vkv$^S*&0UJh7MA%2LH{cW~13a=f}`3g#4m0uR6d zFG0h#a6IcbDCE-2KN)kHPv#&78+mP_PNLbH0tU4?PM8|^2t!++_r*pD4?<>2bE^rC zqG5BGmwF?}4pz6!jHJm04?BnBASjgj94O5lhs`n%n~E^7>KUOlwO~Z+k*_Zfn$qJN z*o+=qVW0kfkyQl^ORF?2c@rW(cj{bwBz&S zjYBe*)VMM-nTgcxVnf+Uw&P9aqryLBm;MvuRiWqmPsql-=4&7Ur@;CCE5Fd#>?In4 zf1ZXwVLAqNdkj;-DP@3f!|;9w2VokzvK1NRMCdocETcn>dKcZ8o|-Q;Zfg&OQ#xTO zdNUJyj|y_75A$MFD~q5eWf{=TWr*l&h~(h}Iy^D^g|fSq37%;m@nA2Ei5}E3+6UAZ zp(7b?)eDX#Q1&+2=atMUKQN!X=tGTb><2J2SyT3<<75F2GQVc*pPQJ=0oK%ldHbPv zxAA&g_R=G~=Vy%ej_kvtK^;aGAqzUU4Ch|VH56>CO^{0aVjuRhFSx$y*hUcaE6lu# zvUe%aH=X%@N>EMn7ybEX)DMVeb{GFsZDB$(zyry=*92ayPSgujpT~J}$k9NL>)NVC z&>p4U3PKdXWn_KnSM({_4)v?(u=Sv|<*_nyx7xA0Y)Xe38TIkSGjUlj2e=fEI-h+(|u>P_d%u6_o? z`4TrHIxR@a9t_egD2wVmpL2{B@hF!Z7|kbnZ7$gJrYK&eW(T66I%G;|2{FpWF!3JI z>3BF4eRzE!IC#D1D{z**iAX5Iuj1yGU@23foW*=3rBfalla?$l=?^ii<6%51a-ISu zBX)_sI1Lq1T7)5N0!pw8ibhT1G=4+5su`M;{zkZ=n_|okCo=ywEKlJ@&%#Oe+ket& zVxvsPmt;{oQ465PH^Qq5hNF!`snxV>1S&k6g+f(ST=fR0aSZkl70SYOa+0yQfe&e>qRPaA1PK-&SHH5m$I=)};9h{v-!6DUPC?ik(E6 zLRcS^zUIe$Z-TQX1uuEbqS*HW@cHCcl?c*=2=a~RR%0WhSyw`t?-4p#!NsRSP1yJta*zNC|Ln6v&= zWb{=MiQ;TIyyG~T>5=GNHC+>&@-q5zE>3_9@CC5^0u$tn7u8 zIHnHzC^7nI4LUFt5Z4sIb2g!?ebAYLjf!x?i+!gG`-O&}v>VV}IndeKAKpekZsS;8 z7jjZ3Qyj?NGY=ix2k4IK!$uLDs>-?cf%TVeF2gl7R2Mf8gT568y$D2pW6y1Y(X9gF z1#%8>6oLrME!F-9bVtq0z5`OEz@BQv#K7bgJ&M&pC;KT7!}j%9 z#Zlc+F2W@yKsoi{J>5`fGHky_8I~imoD(G^2#eCHDiT(-VmP1yj)+ zT;mp^Jn;#=afQSi{cb3R6&%yl8}#$YBvPhElPDDRta_*jkYe7R2Zau$e8X`A{6Da_9g6KmbWZK~$l#XjapsCdW-SzR!ded!4+MK2;uVv`VMUR4Zv)rtsb zDy>?N%}YvqGWW9(RH&Jd)gn^E3245iRJ&L{^7DE+jyX$qU>i|bJ;Qy@je71~48fK&ULAf;28PjmJGk!8@M0Xp&64rEmX z8MVUTJ$IQ$56Cq-v#&F$ws||acXslJh{2|AyxOyfI zfPz*HKnh%7Um1g=(Um<;bpgF`06tQ5U+*L8FNdWr@)2xnD0_J;40`GH{_HWeFyilV zo)zrBf}pDAVF_U^jt2#x2`km-&B_4r9E zh+Oj`gMqwWpTJ#4_JJ>Xy{ViIWN?JNS9G$UFi-j%SzoBzH3VIKkB|M9ixsPRo}l$`3F$mD1EW zh_cy+axTQ2M|1AJc{4fA!wn4FawtP7Y7J#O-iNV5j8DC{9kiY(G%3+Z80Sh02VguO z!r)hdTM1a?5?AHYp@aRU|st;rZ~z~S%483 z(;qPZ1f1hXHi6Q}JQvDam{@gGZbJl33U)g8KnzDkagtP67aIp88SpOM&y4hO$W7)z z(W`N6ShYEZCTURJMY77=AB~kbWtX@om5}Ed<|ju$8}JIU7}aoERCujTZ5YwHcQ_^( zIV;t!&yqwfr$&WvVzOF8n%W0>i`vzn`?-p9e?nyMI@cdfB&#O9A(Wo~9+~AJ3-TuS zOEiTMH5}*>bM{Fa$1>+k;LJbVfrzL%xYuzd@UloCQVd1P!#wDhWm5m%>Gdoy(mZFJ%VkgfVp~8cPB_@0dzE~(*^_GDMw*&0?s!L zgFXSy$|L4^j=41k(xVy6#QdIMRwsHK399+gR)6aK(m8>x?=a>YV`z){t_RWih6J`$ zlcA#Jy41L3Wlvd-18|u&HAkllPBoN0`ing2opUQ2{VwxQ&pNfoUq%MX0thxV5-6~k z1atB;mi@N`dtY*Nw~CgNFwflVF%uvMNyVO`Kp{=w=;#~-)HZ=bwXKZ5`sO4gh}Ml- zgmz>;lr5N!TvP+{278;jXqSR=J12Y6Q082QJt!}G{wZL<>P^uNCr&}C(d;Qr!W^ep z(OD3L911Rd{QhaU&cZ*5!+r91B@k|;RBw>VlBNtxWre@^cTfIT$D>B9G-nKvqs}-0$53R;Fl-y4P{Xai88{gE zP*!QY;@N17?z$McUMN3BTY3QLxrBi(cufK{yJ`kbqQqp7_e5E10}=C46#l+}vXg=p zg`A>JX~D7{W)5CZ&lYEWa}J-P$Qk-ovMS$D+}!}DDF*M`${LiCE`Z^$-UI8|bd*(~ zueAe{QW{!C*NfQ9-UF_52uETg&sB^pqkE?R38!wr7bQ#Q%TUT$oH!Nh&0}sIShvO; z90e5@S0)0sgQ!pl_fds*%8Dp5R*-0#93*jaH$|!M;~JN_=h_H06fxSzdTKLorGrm$ zf1Yp;6!|Gh6T5<3Q)3*PN@=n}coEKKsLzX>I!~MiIYxqT`Eh;vQ9G1>VtHDM%zBYF zR{`O7z|pF*IpyXY!^t>_`KmCHsT*8RO?osU%w*w0Of&qW;#}MPar_ z7zy)Lc18xOiWRl*MD1KhjOG)-aRhU0Nyb-DP-T$?tyEU?8hU37fuXnA2e%Sn5tEyO zH_D(ZgDU#d6W0P^`nzgxXd;QZgp`pWnQFpRM6Ftc zOc?Zw37*_wk4pedNK~UO3I1q5(U?Xst2*^!siC=pMqJSCYLnC%V5U!6)@~ z(0-{%e}47{H9Fg(j4ebIZP+_BCX=22Bx@>*1-XP8wN+#v&c_@!;{=^!P1Vuk>N_!k zEK5QHN#zN`%{z60xj?$XIyNDLMC@s~*=H9r=Sb{}VlEWL>Qwgd+Biqr$F!HIU^zW| z)F3hk>iLjfjtldV zdR@#rY7Ube#rFeWc6t69!qVp>t(X(Vvy%5*0BZ6vy%@ymu#yhLT?%HgsX$X=;!0P+ z0G&m2AsULNy7>K3s48R?ZRQaS!UHJOvKX%_681;Y1#zxsD7&jD;cFP2NpMCE<1~cu z{O#=N>F;pKiTD>IlA;j=HR&%WFN5FDV6!kp8)r%bUN9Ps*8~{gjG>mya=vuv@1uYDuNavjl6UKmoS(&@py30WpmGf5f z@p4kY3olR%7cIKevHBS6P81`C6-OEG#L!L{E0*O$;p;lZlO!}B*0+WTO-im`9EC5Y zjMZH0;)5sl2xw3fl&derz*m4eO*wPTdNQBO;OBP*--E6-q2&QGwD#OJi_YAXIhlF( zn)$?w<1i+jMP9Qm1z8_zF4z}=NCn%S$kC`DNQ`{%*p(-dEODIJq;?;gM@$Owyy*+;A7LV z9zniT_n|n-6E;_+$HlJYt<4f=%a4ehx|YgenT8|Rf^@phJCrC>aW5glZpAcKINBH+Jk;PR|!na9@W+wGPI)_NHZ* zf+4~PrC1eDk06w#Qqpe#I8LTS3DB`I$N)!onu(?*C?&FvrMxFU`%TFd2?@g7wd25S za`WDO1g1Wr)=zp{V=eE?;tjhMSq5ZLj_ZrEI|!1F57GmD;#@zmhMsw|SX>Xt zP1xhou*WEiu`%F;mf=8hod>VNFKr-<*!jw{$A!5DVXx_R*IAGh-0|Ec5K5P-BIoCR z>OY-O_{K{_3l2xRZ(rci$BhnVp6Irs(_%OnkLUjpFdQk=Ux^9`ga0OJ#oZWoaxlb= ztSHT5>KwXYTyG1yYY(G&Sj+T@VNkeb7f_CE$uyk8@SKAo*o_GInV_2vb3|rRtr<`h z=dNA1l^Bf;F)XiKzX5{;^lLzP4qm?O7~oY%8E-roU;$jOGRI%IaM3A{9VS)=G92@E z?Xx>X!Q;k>Y+aY`b6`IwjvaG0y@OY8*bg`?gV`_^tUqewd8`hR9oBCEk^{TaqZ_u0 zx�6fslqOEhRSQJK6rLluuhu+>zDy@VbCsxkb|NX~ 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 Date: Thu, 8 Dec 2022 16:29:03 +0000 Subject: [PATCH 23/44] docs: move solomachine docs into the docs directory. (#2908) --- docs/.vuepress/config.js | 118 +++++++++++------- .../ibc/light-clients/solomachine/concepts.md | 4 +- .../light-clients/solomachine/solomachine.md | 0 .../ibc/light-clients/solomachine/state.md | 1 - .../solomachine/state_transitions.md | 0 5 files changed, 78 insertions(+), 45 deletions(-) rename modules/light-clients/06-solomachine/spec/01_concepts.md => docs/ibc/light-clients/solomachine/concepts.md (95%) rename modules/light-clients/06-solomachine/spec/README.md => docs/ibc/light-clients/solomachine/solomachine.md (100%) rename modules/light-clients/06-solomachine/spec/02_state.md => docs/ibc/light-clients/solomachine/state.md (99%) rename modules/light-clients/06-solomachine/spec/03_state_transitions.md => docs/ibc/light-clients/solomachine/state_transitions.md (100%) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index aead1124617..9c44dcecbb8 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -322,52 +322,86 @@ module.exports = { ], }, { - title: "IBC Light Client Developer Guide", + title: "IBC Light Clients", children: [ { - title: "Overview", - directory: false, - path: "/light-clients/overview.html", - }, - { - title: "ClientState", - directory: false, - path: "light-clients/client-state.html", - }, - { - title: "ConsensusState", - directory: false, - path: "/light-clients/consensus-state.html", - }, - { - title: "Existence/Non-Existence Proofs", - directory: false, - path: "/light-clients/proofs.html", - }, - { - title: "Updates Handling", - directory: false, - path: "/light-clients/update.html", - }, - { - title: "Misbehaviour Handling", - directory: false, - path: "/light-clients/misbehaviour.html", - }, - { - title: "Upgrades Handling", - directory: false, - path: "/light-clients/upgrade.html", - }, - { - title: "Proposal Handling", - directory: false, - path: "/light-clients/proposal.html", + title: "Developer Guide", + directory: true, + path: "/ibc/light-clients", + children: [ + { + title: "Overview", + directory: false, + path: "/ibc/light-clients/overview.html", + }, + { + title: "ClientState", + directory: false, + path: "/ibc/light-clients/client-state.html", + }, + { + title: "ConsensusState", + directory: false, + path: "/ibc/light-clients/consensus-state.html", + }, + { + title: "Existence/Non-Existence Proofs", + directory: false, + path: "/ibc/light-clients/proofs.html", + }, + { + title: "Updates Handling", + directory: false, + path: "/ibc/light-clients/update.html", + }, + { + title: "Misbehaviour Handling", + directory: false, + path: "/ibc/light-clients/misbehaviour.html", + }, + { + title: "Upgrades Handling", + directory: false, + path: "/ibc/light-clients/upgrade.html", + }, + { + title: "Proposal Handling", + directory: false, + path: "/ibc/light-clients/proposal.html", + }, + { + title: "Genesis Handling", + directory: false, + path: "/ibc/light-clients/genesis.html", + }, + ] }, { - title: "Genesis Handling", - directory: false, - path: "/light-clients/genesis.html", + title: "Solomachine", + directory: true, + path: "/ibc/light-clients/solomachine", + children: [ + { + title: "Solomachine", + directory: false, + path: "/ibc/light-clients/solomachine/solomachine.html", + }, + { + title: "Concepts", + directory: false, + path: "/ibc/light-clients/solomachine/concepts.html", + }, + { + title: "State", + directory: false, + path: "/ibc/light-clients/solomachine/state.html", + }, + { + title: "State Transitions", + directory: false, + path: "/ibc/light-clients/solomachine/state_transitions.html", + }, + ], }, ], }, diff --git a/modules/light-clients/06-solomachine/spec/01_concepts.md b/docs/ibc/light-clients/solomachine/concepts.md similarity index 95% rename from modules/light-clients/06-solomachine/spec/01_concepts.md rename to docs/ibc/light-clients/solomachine/concepts.md index c5df3f7e1b4..ac74e6e807d 100644 --- a/modules/light-clients/06-solomachine/spec/01_concepts.md +++ b/docs/ibc/light-clients/solomachine/concepts.md @@ -56,7 +56,7 @@ data := &ClientStateData{ dataBz, err := cdc.Marshal(data) ``` -The helper functions `...DataBytes()` in [proof.go](../proof.go) handle this +The helper functions `...DataBytes()` in [proof.go](../../../../modules/light-clients/06-solomachine/proof.go) handle this functionality. 2. Construct the `SignBytes` and marshal it. @@ -75,7 +75,7 @@ signBytes := &SignBytes{ signBz, err := cdc.Marshal(signBytes) ``` -The helper functions `...SignBytes()` in [proof.go](../proof.go) handle this functionality. +The helper functions `...SignBytes()` in [proof.go](../../../../modules/light-clients/06-solomachine/proof.go) handle this functionality. The `DataType` field is used to disambiguate what type of data was signed to prevent potential proto encoding overlap. diff --git a/modules/light-clients/06-solomachine/spec/README.md b/docs/ibc/light-clients/solomachine/solomachine.md similarity index 100% rename from modules/light-clients/06-solomachine/spec/README.md rename to docs/ibc/light-clients/solomachine/solomachine.md diff --git a/modules/light-clients/06-solomachine/spec/02_state.md b/docs/ibc/light-clients/solomachine/state.md similarity index 99% rename from modules/light-clients/06-solomachine/spec/02_state.md rename to docs/ibc/light-clients/solomachine/state.md index 51cb1f058c6..a4b9f87cf8c 100644 --- a/modules/light-clients/06-solomachine/spec/02_state.md +++ b/docs/ibc/light-clients/solomachine/state.md @@ -6,4 +6,3 @@ order: 2 The solo machine light client will only store consensus states for each update by a header or a governance proposal. The latest client state is also maintained in the store. - diff --git a/modules/light-clients/06-solomachine/spec/03_state_transitions.md b/docs/ibc/light-clients/solomachine/state_transitions.md similarity index 100% rename from modules/light-clients/06-solomachine/spec/03_state_transitions.md rename to docs/ibc/light-clients/solomachine/state_transitions.md From 514ad3956048392beb1b2fe52a8365c1a5b8b27e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Dec 2022 16:40:17 +0000 Subject: [PATCH 24/44] deps: bump technote-space/get-diff-action from 6.1.1 to 6.1.2 (#2899) --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/test.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index cf5d9be0093..9cdbe5618cf 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 - - uses: technote-space/get-diff-action@v6.1.1 + - uses: technote-space/get-diff-action@v6.1.2 with: PATTERNS: | **/**.go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 88343c0c3f2..a4be1f6697a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,7 +42,7 @@ jobs: - uses: actions/setup-go@v3 with: go-version: 1.18 - - uses: technote-space/get-diff-action@v6.1.1 + - uses: technote-space/get-diff-action@v6.1.2 id: git_diff with: PATTERNS: | @@ -98,7 +98,7 @@ jobs: - uses: actions/setup-go@v3 with: go-version: 1.18 - - uses: technote-space/get-diff-action@v6.1.1 + - uses: technote-space/get-diff-action@v6.1.2 with: PATTERNS: | **/**.go @@ -122,7 +122,7 @@ jobs: needs: tests steps: - uses: actions/checkout@v3 - - uses: technote-space/get-diff-action@v6.1.1 + - uses: technote-space/get-diff-action@v6.1.2 with: PATTERNS: | **/**.go From d1fbdbd232c9a1aece1f6a4004fe0d17fc2b4243 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Dec 2022 16:45:53 +0000 Subject: [PATCH 25/44] deps: bump github.com/cosmos/cosmos-proto from 1.0.0-alpha8 to 1.0.0-beta.1 (#2870) --- e2e/go.mod | 2 +- e2e/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/e2e/go.mod b/e2e/go.mod index 449d01e1068..80483f66c4f 100644 --- a/e2e/go.mod +++ b/e2e/go.mod @@ -49,7 +49,7 @@ require ( github.com/cockroachdb/apd/v2 v2.0.2 // indirect github.com/confio/ics23/go v0.9.0 // indirect github.com/cosmos/btcutil v1.0.4 // indirect - github.com/cosmos/cosmos-proto v1.0.0-alpha8 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.1 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gorocksdb v1.2.0 // indirect github.com/cosmos/iavl v0.19.4 // indirect diff --git a/e2e/go.sum b/e2e/go.sum index 54465425499..1f9b771e33b 100644 --- a/e2e/go.sum +++ b/e2e/go.sum @@ -208,8 +208,8 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cosmos/btcutil v1.0.4 h1:n7C2ngKXo7UC9gNyMNLbzqz7Asuf+7Qv4gnX/rOdQ44= github.com/cosmos/btcutil v1.0.4/go.mod h1:Ffqc8Hn6TJUdDgHBwIZLtrLQC1KdJ9jGJl/TvgUaxbU= -github.com/cosmos/cosmos-proto v1.0.0-alpha8 h1:d3pCRuMYYvGA5bM0ZbbjKn+AoQD4A7dyNG2wzwWalUw= -github.com/cosmos/cosmos-proto v1.0.0-alpha8/go.mod h1:6/p+Bc4O8JKeZqe0VqUGTX31eoYqemTT4C1hLCWsO7I= +github.com/cosmos/cosmos-proto v1.0.0-beta.1 h1:iDL5qh++NoXxG8hSy93FdYJut4XfgbShIocllGaXx/0= +github.com/cosmos/cosmos-proto v1.0.0-beta.1/go.mod h1:8k2GNZghi5sDRFw/scPL8gMSowT1vDA+5ouxL8GjaUE= github.com/cosmos/cosmos-sdk v0.46.6 h1:K9EZsqOZ2jQX3bIQUpn7Hk/YCoaJWRLU56PzvpX8INk= github.com/cosmos/cosmos-sdk v0.46.6/go.mod h1:JNklMfXo7MhDF1j/jxZCmDyOYyqhVoKB22e8p1ATEqA= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= diff --git a/go.mod b/go.mod index 269cdb3b01b..caf7821c07e 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( cosmossdk.io/math v1.0.0-beta.3 github.com/armon/go-metrics v0.4.1 github.com/confio/ics23/go v0.9.0 - github.com/cosmos/cosmos-proto v1.0.0-alpha8 + github.com/cosmos/cosmos-proto v1.0.0-beta.1 github.com/cosmos/cosmos-sdk v0.46.6 github.com/gogo/protobuf v1.3.3 github.com/golang/protobuf v1.5.2 diff --git a/go.sum b/go.sum index 563d0b878b6..405893f96c6 100644 --- a/go.sum +++ b/go.sum @@ -237,8 +237,8 @@ github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1 github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cosmos/btcutil v1.0.4 h1:n7C2ngKXo7UC9gNyMNLbzqz7Asuf+7Qv4gnX/rOdQ44= github.com/cosmos/btcutil v1.0.4/go.mod h1:Ffqc8Hn6TJUdDgHBwIZLtrLQC1KdJ9jGJl/TvgUaxbU= -github.com/cosmos/cosmos-proto v1.0.0-alpha8 h1:d3pCRuMYYvGA5bM0ZbbjKn+AoQD4A7dyNG2wzwWalUw= -github.com/cosmos/cosmos-proto v1.0.0-alpha8/go.mod h1:6/p+Bc4O8JKeZqe0VqUGTX31eoYqemTT4C1hLCWsO7I= +github.com/cosmos/cosmos-proto v1.0.0-beta.1 h1:iDL5qh++NoXxG8hSy93FdYJut4XfgbShIocllGaXx/0= +github.com/cosmos/cosmos-proto v1.0.0-beta.1/go.mod h1:8k2GNZghi5sDRFw/scPL8gMSowT1vDA+5ouxL8GjaUE= github.com/cosmos/cosmos-sdk v0.46.6 h1:K9EZsqOZ2jQX3bIQUpn7Hk/YCoaJWRLU56PzvpX8INk= github.com/cosmos/cosmos-sdk v0.46.6/go.mod h1:JNklMfXo7MhDF1j/jxZCmDyOYyqhVoKB22e8p1ATEqA= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= From e1c29e58dd05ebdc0f47faaaf91d468c017d6e8e Mon Sep 17 00:00:00 2001 From: Charly Date: Fri, 9 Dec 2022 10:09:17 +0100 Subject: [PATCH 26/44] update README (#2910) --- README.md | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index cc0f2915bc6..87eee687f91 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,13 @@ We have detailed documents for contributors wishing to [contribute code to ibc-g To report a security vulnerability, see our [bug bounty program](https://hackerone.com/cosmos). -## Contents of this repo +## Audits + +The following audits have been performed on the `ibc-go` source code: + +- [ICS27 Interchain Accounts](https://github.com/cosmos/ibc-go/tree/main/docs/apps/interchain-accounts/audits) by Trail of Bits + +## Quick Navigation 1. **[Core IBC Implementation](https://github.com/cosmos/ibc-go/tree/main/modules/core)** @@ -114,14 +120,21 @@ To report a security vulnerability, see our [bug bounty program](https://hackero 2.2 [ICS 27 Interchain Accounts](https://github.com/cosmos/ibc-go/tree/main/modules/apps/27-interchain-accounts) -3. **Light Clients** +3. **Middleware** + + 3.1 [ICS 29 Fee Middleware](https://github.com/cosmos/ibc-go/tree/main/modules/apps/29-fee) + +4. **Light Clients** + + 4.1 [ICS 07 Tendermint](https://github.com/cosmos/ibc-go/tree/main/modules/light-clients/07-tendermint) - 3.1 [ICS 07 Tendermint](https://github.com/cosmos/ibc-go/tree/main/modules/light-clients/07-tendermint) + 4.2 [ICS 06 Solo Machine](https://github.com/cosmos/ibc-go/tree/main/modules/light-clients/06-solomachine) - 3.2 [ICS 06 Solo Machine](https://github.com/cosmos/ibc-go/tree/main/modules/light-clients/06-solomachine) +5. **[E2E Integration Tests](https://github.com/cosmos/ibc-go/tree/main/e2e)** ## Documentation and Resources - [IBC Website](https://ibcprotocol.dev/) -- [IBC Specification](https://github.com/cosmos/ibc) +- [IBC Protocol Specification](https://github.com/cosmos/ibc) - [Documentation](https://ibc.cosmos.network/main/ibc/overview.html) +- [Interchain Developer Academy](https://tutorials.cosmos.network/academy/3-ibc/) \ No newline at end of file From 267359bde79cb66599105804e001a7285024d787 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Fri, 9 Dec 2022 10:32:34 +0100 Subject: [PATCH 27/44] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b85f401700f..47a484e5a3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,7 +73,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### State Machine Breaking -* (light-clients/07-tendermint) [\#2554](https://github.com/cosmos/ibc-go/pull/2554) Forbid negative values for `TrustingPeriod`, `UnbondingPeriod` and `MaxClockDrift` (as specified in ICS-07). +* (light-clients/07-tendermint) [\#2555](https://github.com/cosmos/ibc-go/pull/2555) Forbid negative values for `TrustingPeriod`, `UnbondingPeriod` and `MaxClockDrift` (as specified in ICS-07). * (06-solomachine) [\#2744](https://github.com/cosmos/ibc-go/pull/2744) `Misbehaviour.ValidateBasic()` now only enforces that signature data does not match when the signature paths are different. * (06-solomachine) [\#2748](https://github.com/cosmos/ibc-go/pull/2748) Adding sentinel value for header path in 06-solomachine. From a5ff8644fc833de874baaacea2f057717b90edc9 Mon Sep 17 00:00:00 2001 From: Charly Date: Mon, 12 Dec 2022 13:40:23 +0100 Subject: [PATCH 28/44] chore: update ibctest to latest commit (#2909) * update chainconfig * update to commit fixing broadcastTx * update to use SDK default cointype 118 * update so relayer tag isn't required --- .github/workflows/e2e-test-workflow-call.yml | 2 +- e2e/go.mod | 2 +- e2e/go.sum | 6 ++++-- e2e/testconfig/testconfig.go | 3 ++- e2e/tests/core/03-connection/connection_test.go | 2 +- e2e/tests/core/client_test.go | 2 +- e2e/tests/interchain_accounts/base_test.go | 4 ++-- e2e/tests/interchain_accounts/gov_test.go | 2 +- e2e/tests/interchain_accounts/groups_test.go | 4 ++-- e2e/tests/interchain_accounts/incentivized_test.go | 4 ++-- .../interchain_accounts/intertx_incentivized_test.go | 4 ++-- e2e/tests/interchain_accounts/intertx_test.go | 4 ++-- e2e/tests/transfer/base_test.go | 4 ++-- e2e/tests/transfer/incentivized_test.go | 10 +++++----- e2e/tests/upgrades/upgrade_test.go | 4 ++-- e2e/testsuite/testsuite.go | 6 +++--- 16 files changed, 33 insertions(+), 30 deletions(-) diff --git a/.github/workflows/e2e-test-workflow-call.yml b/.github/workflows/e2e-test-workflow-call.yml index f5ac978d19a..5d27698706a 100644 --- a/.github/workflows/e2e-test-workflow-call.yml +++ b/.github/workflows/e2e-test-workflow-call.yml @@ -33,7 +33,7 @@ on: type: string relayer-tag: description: 'The tag to use for the relayer' - required: true + required: false default: "v2.1.2" type: string build-and-push-docker-image: diff --git a/e2e/go.mod b/e2e/go.mod index 80483f66c4f..55ad7e415f7 100644 --- a/e2e/go.mod +++ b/e2e/go.mod @@ -10,7 +10,7 @@ require ( github.com/cosmos/interchain-accounts v0.4.0 github.com/docker/docker v20.10.19+incompatible github.com/gogo/protobuf v1.3.3 - github.com/strangelove-ventures/ibctest/v6 v6.0.0-20221005154709-b642157674bc + github.com/strangelove-ventures/ibctest/v6 v6.0.0-20221209152048-e8388a0b91fd github.com/stretchr/testify v1.8.1 github.com/tendermint/tendermint v0.34.23 go.uber.org/zap v1.23.0 diff --git a/e2e/go.sum b/e2e/go.sum index 1f9b771e33b..a2d5c3a3e94 100644 --- a/e2e/go.sum +++ b/e2e/go.sum @@ -832,8 +832,10 @@ github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+eg github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/strangelove-ventures/go-subkey v1.0.7 h1:cOP/Lajg3uxV/tvspu0m6+0Cu+DJgygkEAbx/s+f35I= github.com/strangelove-ventures/go-subkey v1.0.7/go.mod h1:E34izOIEm+sZ1YmYawYRquqBQWeZBjVB4pF7bMuhc1c= -github.com/strangelove-ventures/ibctest/v6 v6.0.0-20221005154709-b642157674bc h1:IIyDsxY/Rxg1aEtUUUcSKWIdgTjjJwMH6B/JggvlYbA= -github.com/strangelove-ventures/ibctest/v6 v6.0.0-20221005154709-b642157674bc/go.mod h1:Z4se9RL/mP5DbZ7kEGoDrjQJU06yEs5+Bznt41Uvi5o= +github.com/strangelove-ventures/ibctest/v6 v6.0.0-20221202162429-9359b061fbe5 h1:nzNdAKDDCuhDYT+Rk9SjmLOKUmg6KEMnTHm/xoHXq6Q= +github.com/strangelove-ventures/ibctest/v6 v6.0.0-20221202162429-9359b061fbe5/go.mod h1:l3Q2U/8aRyZrOqXp45nhxSjeXgBB+qROwu+L0vIJmUw= +github.com/strangelove-ventures/ibctest/v6 v6.0.0-20221209152048-e8388a0b91fd h1:67pNLuMmq8L8BW8WShjtMoL0KFONLpOVYQxb2HY8IlQ= +github.com/strangelove-ventures/ibctest/v6 v6.0.0-20221209152048-e8388a0b91fd/go.mod h1:l3Q2U/8aRyZrOqXp45nhxSjeXgBB+qROwu+L0vIJmUw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= diff --git a/e2e/testconfig/testconfig.go b/e2e/testconfig/testconfig.go index 544bb34ae94..d4cb5eab329 100644 --- a/e2e/testconfig/testconfig.go +++ b/e2e/testconfig/testconfig.go @@ -36,7 +36,7 @@ const ( // defaultBinary is the default binary that will be used by the chains. defaultBinary = "simd" // defaultRlyTag is the tag that will be used if no relayer tag is specified. - // all images are here https://github.com/cosmos/relayer/pkgs/container/relayer/versions + // all images are here https://github.com/cosmos/relayer/pkgs/container/relayer/versions defaultRlyTag = "v2.1.2" // defaultChainTag is the tag that will be used for the chains if none is specified. defaultChainTag = "main" @@ -166,6 +166,7 @@ func newDefaultSimappConfig(cc ChainConfig, name, chainID, denom string) ibc.Cha }, Bin: cc.Binary, Bech32Prefix: "cosmos", + CoinType: fmt.Sprint(sdk.GetConfig().GetCoinType()), Denom: denom, GasPrices: fmt.Sprintf("0.00%s", denom), GasAdjustment: 1.3, diff --git a/e2e/tests/core/03-connection/connection_test.go b/e2e/tests/core/03-connection/connection_test.go index 3e962e37e09..3cefdcb51a5 100644 --- a/e2e/tests/core/03-connection/connection_test.go +++ b/e2e/tests/core/03-connection/connection_test.go @@ -10,7 +10,7 @@ import ( paramsproposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal" "github.com/strangelove-ventures/ibctest/v6/ibc" - "github.com/strangelove-ventures/ibctest/v6/test" + test "github.com/strangelove-ventures/ibctest/v6/testutil" "github.com/stretchr/testify/suite" "github.com/cosmos/ibc-go/e2e/testsuite" diff --git a/e2e/tests/core/client_test.go b/e2e/tests/core/client_test.go index 4b2b2b64308..649e1e4f176 100644 --- a/e2e/tests/core/client_test.go +++ b/e2e/tests/core/client_test.go @@ -8,7 +8,7 @@ import ( "time" "github.com/strangelove-ventures/ibctest/v6/ibc" - "github.com/strangelove-ventures/ibctest/v6/test" + test "github.com/strangelove-ventures/ibctest/v6/testutil" "github.com/stretchr/testify/suite" "github.com/cosmos/ibc-go/e2e/testsuite" diff --git a/e2e/tests/interchain_accounts/base_test.go b/e2e/tests/interchain_accounts/base_test.go index ccf31164aa2..551c948c2e2 100644 --- a/e2e/tests/interchain_accounts/base_test.go +++ b/e2e/tests/interchain_accounts/base_test.go @@ -5,10 +5,10 @@ import ( "testing" "time" - ibctest "github.com/strangelove-ventures/ibctest/v6" + "github.com/strangelove-ventures/ibctest/v6" "github.com/strangelove-ventures/ibctest/v6/chain/cosmos" "github.com/strangelove-ventures/ibctest/v6/ibc" - "github.com/strangelove-ventures/ibctest/v6/test" + test "github.com/strangelove-ventures/ibctest/v6/testutil" "github.com/stretchr/testify/suite" "golang.org/x/mod/semver" diff --git a/e2e/tests/interchain_accounts/gov_test.go b/e2e/tests/interchain_accounts/gov_test.go index f0b18602bc3..ac6b32382cf 100644 --- a/e2e/tests/interchain_accounts/gov_test.go +++ b/e2e/tests/interchain_accounts/gov_test.go @@ -11,7 +11,7 @@ import ( "github.com/gogo/protobuf/proto" "github.com/strangelove-ventures/ibctest/v6" "github.com/strangelove-ventures/ibctest/v6/ibc" - "github.com/strangelove-ventures/ibctest/v6/test" + test "github.com/strangelove-ventures/ibctest/v6/testutil" "github.com/stretchr/testify/suite" "github.com/cosmos/ibc-go/e2e/testsuite" diff --git a/e2e/tests/interchain_accounts/groups_test.go b/e2e/tests/interchain_accounts/groups_test.go index bf58b49f977..a5c1d79fda2 100644 --- a/e2e/tests/interchain_accounts/groups_test.go +++ b/e2e/tests/interchain_accounts/groups_test.go @@ -9,9 +9,9 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" grouptypes "github.com/cosmos/cosmos-sdk/x/group" "github.com/gogo/protobuf/proto" - ibctest "github.com/strangelove-ventures/ibctest/v6" + "github.com/strangelove-ventures/ibctest/v6" "github.com/strangelove-ventures/ibctest/v6/ibc" - "github.com/strangelove-ventures/ibctest/v6/test" + test "github.com/strangelove-ventures/ibctest/v6/testutil" "github.com/stretchr/testify/suite" "github.com/cosmos/ibc-go/e2e/testsuite" diff --git a/e2e/tests/interchain_accounts/incentivized_test.go b/e2e/tests/interchain_accounts/incentivized_test.go index cfee87218ce..929aec24192 100644 --- a/e2e/tests/interchain_accounts/incentivized_test.go +++ b/e2e/tests/interchain_accounts/incentivized_test.go @@ -8,10 +8,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/gogo/protobuf/proto" - ibctest "github.com/strangelove-ventures/ibctest/v6" + "github.com/strangelove-ventures/ibctest/v6" "github.com/strangelove-ventures/ibctest/v6/chain/cosmos" "github.com/strangelove-ventures/ibctest/v6/ibc" - "github.com/strangelove-ventures/ibctest/v6/test" + test "github.com/strangelove-ventures/ibctest/v6/testutil" "github.com/stretchr/testify/suite" "github.com/cosmos/ibc-go/e2e/testconfig" diff --git a/e2e/tests/interchain_accounts/intertx_incentivized_test.go b/e2e/tests/interchain_accounts/intertx_incentivized_test.go index 063c3f30be6..4e5844b78ae 100644 --- a/e2e/tests/interchain_accounts/intertx_incentivized_test.go +++ b/e2e/tests/interchain_accounts/intertx_incentivized_test.go @@ -7,9 +7,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" intertxtypes "github.com/cosmos/interchain-accounts/x/inter-tx/types" - ibctest "github.com/strangelove-ventures/ibctest/v6" + "github.com/strangelove-ventures/ibctest/v6" "github.com/strangelove-ventures/ibctest/v6/ibc" - "github.com/strangelove-ventures/ibctest/v6/test" + test "github.com/strangelove-ventures/ibctest/v6/testutil" "github.com/stretchr/testify/suite" "github.com/cosmos/ibc-go/e2e/testvalues" diff --git a/e2e/tests/interchain_accounts/intertx_test.go b/e2e/tests/interchain_accounts/intertx_test.go index 8340b2ed8a8..7d30aa5d5e1 100644 --- a/e2e/tests/interchain_accounts/intertx_test.go +++ b/e2e/tests/interchain_accounts/intertx_test.go @@ -4,10 +4,10 @@ import ( "context" "testing" - ibctest "github.com/strangelove-ventures/ibctest/v6" + "github.com/strangelove-ventures/ibctest/v6" "github.com/strangelove-ventures/ibctest/v6/chain/cosmos" "github.com/strangelove-ventures/ibctest/v6/ibc" - "github.com/strangelove-ventures/ibctest/v6/test" + test "github.com/strangelove-ventures/ibctest/v6/testutil" "github.com/stretchr/testify/suite" sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/e2e/tests/transfer/base_test.go b/e2e/tests/transfer/base_test.go index ef057456550..f2c7bb7836d 100644 --- a/e2e/tests/transfer/base_test.go +++ b/e2e/tests/transfer/base_test.go @@ -8,7 +8,7 @@ import ( paramsproposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal" "github.com/strangelove-ventures/ibctest/v6/ibc" - "github.com/strangelove-ventures/ibctest/v6/test" + test "github.com/strangelove-ventures/ibctest/v6/testutil" "github.com/stretchr/testify/suite" "github.com/cosmos/ibc-go/e2e/testsuite" @@ -194,7 +194,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Timeout_Nonincentivized() { } t.Run("IBC transfer packet timesout", func(t *testing.T) { - tx, err := chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName, chainBWalletAmount, testvalues.ImmediatelyTimeout()) + tx, err := chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName, chainBWalletAmount, ibc.TransferOptions{Timeout: testvalues.ImmediatelyTimeout()}) s.Require().NoError(err) s.Require().NoError(tx.Validate(), "source ibc transfer tx is invalid") time.Sleep(time.Nanosecond * 1) // want it to timeout immediately diff --git a/e2e/tests/transfer/incentivized_test.go b/e2e/tests/transfer/incentivized_test.go index 6ba4a32a4df..7af218ebce5 100644 --- a/e2e/tests/transfer/incentivized_test.go +++ b/e2e/tests/transfer/incentivized_test.go @@ -7,7 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/strangelove-ventures/ibctest/v6/ibc" - "github.com/strangelove-ventures/ibctest/v6/test" + test "github.com/strangelove-ventures/ibctest/v6/testutil" "github.com/stretchr/testify/suite" "github.com/cosmos/ibc-go/e2e/testvalues" @@ -73,7 +73,7 @@ func (s *IncentivizedTransferTestSuite) TestMsgPayPacketFee_AsyncSingleSender_Su } t.Run("send IBC transfer", func(t *testing.T) { - chainATx, err = chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName, walletAmount, nil) + chainATx, err = chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName, walletAmount, ibc.TransferOptions{}) s.Require().NoError(err) s.Require().NoError(chainATx.Validate(), "chain-a ibc transfer tx is invalid") }) @@ -417,7 +417,7 @@ func (s *IncentivizedTransferTestSuite) TestMsgPayPacketFee_SingleSender_TimesOu } t.Run("Send IBC transfer", func(t *testing.T) { - chainATx, err = chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName, chainBWalletAmount, testvalues.ImmediatelyTimeout()) + chainATx, err = chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName, chainBWalletAmount, ibc.TransferOptions{Timeout: testvalues.ImmediatelyTimeout()}) s.Require().NoError(err) s.Require().NoError(chainATx.Validate(), "source ibc transfer tx is invalid") time.Sleep(time.Nanosecond * 1) // want it to timeout immediately @@ -516,7 +516,7 @@ func (s *IncentivizedTransferTestSuite) TestPayPacketFeeAsync_SingleSender_NoCou t.Run("send IBC transfer", func(t *testing.T) { var err error - chainATx, err = chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName, chainBWalletAmount, nil) + chainATx, err = chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName, chainBWalletAmount, ibc.TransferOptions{}) s.Require().NoError(err) s.Require().NoError(chainATx.Validate(), "source ibc transfer tx is invalid") }) @@ -636,7 +636,7 @@ func (s *IncentivizedTransferTestSuite) TestMsgPayPacketFee_AsyncMultipleSenders } t.Run("send IBC transfer", func(t *testing.T) { - chainATx, err = chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet1.KeyName, walletAmount1, nil) + chainATx, err = chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet1.KeyName, walletAmount1, ibc.TransferOptions{}) s.Require().NoError(err) s.Require().NoError(chainATx.Validate(), "chain-a ibc transfer tx is invalid") }) diff --git a/e2e/tests/upgrades/upgrade_test.go b/e2e/tests/upgrades/upgrade_test.go index 3baf00b6698..dc74d2047e4 100644 --- a/e2e/tests/upgrades/upgrade_test.go +++ b/e2e/tests/upgrades/upgrade_test.go @@ -12,10 +12,10 @@ import ( v6upgrades "github.com/cosmos/interchain-accounts/app/upgrades/v6" intertxtypes "github.com/cosmos/interchain-accounts/x/inter-tx/types" "github.com/gogo/protobuf/proto" - ibctest "github.com/strangelove-ventures/ibctest/v6" + "github.com/strangelove-ventures/ibctest/v6" "github.com/strangelove-ventures/ibctest/v6/chain/cosmos" "github.com/strangelove-ventures/ibctest/v6/ibc" - "github.com/strangelove-ventures/ibctest/v6/test" + test "github.com/strangelove-ventures/ibctest/v6/testutil" "github.com/stretchr/testify/suite" "golang.org/x/mod/semver" diff --git a/e2e/testsuite/testsuite.go b/e2e/testsuite/testsuite.go index f628f6154ce..77c9482bf8a 100644 --- a/e2e/testsuite/testsuite.go +++ b/e2e/testsuite/testsuite.go @@ -20,8 +20,8 @@ import ( "github.com/strangelove-ventures/ibctest/v6" "github.com/strangelove-ventures/ibctest/v6/chain/cosmos" "github.com/strangelove-ventures/ibctest/v6/ibc" - "github.com/strangelove-ventures/ibctest/v6/test" "github.com/strangelove-ventures/ibctest/v6/testreporter" + test "github.com/strangelove-ventures/ibctest/v6/testutil" "github.com/stretchr/testify/suite" "go.uber.org/zap" "go.uber.org/zap/zaptest" @@ -76,8 +76,8 @@ type GRPCClients struct { InterTxQueryClient intertxtypes.QueryClient // SDK query clients - GovQueryClient govtypesv1beta1.QueryClient - GovQueryClientV1 govtypesv1.QueryClient + GovQueryClient govtypesv1beta1.QueryClient + GovQueryClientV1 govtypesv1.QueryClient GroupsQueryClient grouptypes.QueryClient ParamsQueryClient paramsproposaltypes.QueryClient AuthQueryClient authtypes.QueryClient From bfe7ee88258ee6cd0a5077e59cf78492ccf1a205 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Mon, 12 Dec 2022 13:22:53 +0000 Subject: [PATCH 29/44] Fixing markdown link (#2924) --- docs/apps/interchain-accounts/legacy/integration.md | 4 ++-- docs/ibc/light-clients/solomachine/solomachine.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/apps/interchain-accounts/legacy/integration.md b/docs/apps/interchain-accounts/legacy/integration.md index 4a2f99fd692..55613a29739 100644 --- a/docs/apps/interchain-accounts/legacy/integration.md +++ b/docs/apps/interchain-accounts/legacy/integration.md @@ -157,7 +157,7 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino As described above, the Interchain Accounts application module is structured to support the ability of exclusively enabling controller or host functionality. This can be achieved by simply omitting either controller or host `Keeper` from the Interchain Accounts `NewAppModule` constructor function, and mounting only the desired submodule via the `IBCRouter`. -Alternatively, submodules can be enabled and disabled dynamically using [on-chain parameters](./parameters.md). +Alternatively, submodules can be enabled and disabled dynamically using [on-chain parameters](../parameters.md). The following snippets show basic examples of statically disabling submodules using `app.go`. @@ -192,4 +192,4 @@ icaControllerStack := icacontroller.NewIBCMiddleware(icaAuthIBCModule, app.ICACo ibcRouter. AddRoute(icacontrollertypes.SubModuleName, icaControllerStack). AddRoute(icaauthtypes.ModuleName, icaControllerStack) // Note, the authentication module is routed to the top level of the middleware stack -``` \ No newline at end of file +``` diff --git a/docs/ibc/light-clients/solomachine/solomachine.md b/docs/ibc/light-clients/solomachine/solomachine.md index 0879f1bd2be..6eff631c709 100644 --- a/docs/ibc/light-clients/solomachine/solomachine.md +++ b/docs/ibc/light-clients/solomachine/solomachine.md @@ -20,6 +20,6 @@ diversifier, and timestamp. ## Contents -1. **[Concepts](01_concepts.md)** -2. **[State](02_state.md)** -3. **[State Transitions](03_state_transitions.md)** +1. **[Concepts](./concepts.md)** +2. **[State](./state.md)** +3. **[State Transitions](./state_transitions.md)** From a322103a1da9477c8e8d52fa4a06953b239d373b Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 12 Dec 2022 15:16:40 +0100 Subject: [PATCH 30/44] post v6.0.0 release chores (#2919) --- .../release-v2.5.x/transfer-chain-a.json | 2 +- .../release-v2.5.x/transfer-chain-b.json | 2 +- .../release-v3.4.x/transfer-chain-a.json | 2 +- .../release-v3.4.x/transfer-chain-b.json | 2 +- .../incentivized-transfer-chain-a.json | 2 +- .../incentivized-transfer-chain-b.json | 2 +- .../release-v4.2.x/transfer-chain-a.json | 2 +- .../release-v4.2.x/transfer-chain-b.json | 2 +- .../release-v5.0.x/client-chain-a.json | 2 +- .../release-v5.0.x/client-chain-b.json | 2 +- .../release-v5.0.x/connection-chain-a.json | 2 +- .../release-v5.0.x/connection-chain-b.json | 2 +- .../incentivized-transfer-chain-a.json | 2 +- .../incentivized-transfer-chain-b.json | 2 +- .../release-v5.0.x/transfer-chain-a.json | 2 +- .../release-v5.0.x/transfer-chain-b.json | 2 +- .../incentivized-transfer-chain-a.json | 2 +- .../incentivized-transfer-chain-b.json | 2 +- .../release-v5.1.x/transfer-chain-a.json | 2 +- .../release-v5.1.x/transfer-chain-b.json | 2 +- .github/workflows/e2e-manual-icad.yaml | 4 +- .github/workflows/e2e-manual-simd.yaml | 6 +- .github/workflows/e2e-upgrade.yaml | 2 +- CHANGELOG.md | 91 ++++++++++++------- README.md | 2 +- RELEASES.md | 2 + docs/.vuepress/config.js | 4 + docs/apps/interchain-accounts/messages.md | 2 +- docs/ibc/light-clients/client-state.md | 10 +- docs/roadmap/roadmap.md | 35 ++----- docs/versions | 1 + e2e/go.mod | 3 +- e2e/go.sum | 1 + 33 files changed, 109 insertions(+), 94 deletions(-) diff --git a/.github/compatibility-test-matrices/release-v2.5.x/transfer-chain-a.json b/.github/compatibility-test-matrices/release-v2.5.x/transfer-chain-a.json index ac062eb3cbb..b33dc89c7ea 100644 --- a/.github/compatibility-test-matrices/release-v2.5.x/transfer-chain-a.json +++ b/.github/compatibility-test-matrices/release-v2.5.x/transfer-chain-a.json @@ -1,6 +1,6 @@ { "chain-a": ["release-v2.5.x"], - "chain-b": ["release-v2.5.x", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-b": ["release-v2.5.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "entrypoint": ["TestTransferTestSuite"], "test": [ "TestMsgTransfer_Succeeds_Nonincentivized", diff --git a/.github/compatibility-test-matrices/release-v2.5.x/transfer-chain-b.json b/.github/compatibility-test-matrices/release-v2.5.x/transfer-chain-b.json index 4eea85c0e92..49c81a4e373 100644 --- a/.github/compatibility-test-matrices/release-v2.5.x/transfer-chain-b.json +++ b/.github/compatibility-test-matrices/release-v2.5.x/transfer-chain-b.json @@ -1,5 +1,5 @@ { - "chain-a": ["release-v2.5.x", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-a": ["release-v2.5.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "chain-b": ["release-v2.5.x"], "entrypoint": ["TestTransferTestSuite"], "test": [ diff --git a/.github/compatibility-test-matrices/release-v3.4.x/transfer-chain-a.json b/.github/compatibility-test-matrices/release-v3.4.x/transfer-chain-a.json index b6a5c57b874..9755c19e8d9 100644 --- a/.github/compatibility-test-matrices/release-v3.4.x/transfer-chain-a.json +++ b/.github/compatibility-test-matrices/release-v3.4.x/transfer-chain-a.json @@ -1,6 +1,6 @@ { "chain-a": ["release-v3.4.x"], - "chain-b": ["release-v3.4.x", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-b": ["release-v3.4.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "entrypoint": ["TestTransferTestSuite"], "test": [ "TestMsgTransfer_Succeeds_Nonincentivized", diff --git a/.github/compatibility-test-matrices/release-v3.4.x/transfer-chain-b.json b/.github/compatibility-test-matrices/release-v3.4.x/transfer-chain-b.json index 649949b7518..c2c84014314 100644 --- a/.github/compatibility-test-matrices/release-v3.4.x/transfer-chain-b.json +++ b/.github/compatibility-test-matrices/release-v3.4.x/transfer-chain-b.json @@ -1,5 +1,5 @@ { - "chain-a": ["release-v3.4.x", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-a": ["release-v3.4.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "chain-b": ["release-v3.4.x"], "entrypoint": ["TestTransferTestSuite"], "test": [ diff --git a/.github/compatibility-test-matrices/release-v4.2.x/incentivized-transfer-chain-a.json b/.github/compatibility-test-matrices/release-v4.2.x/incentivized-transfer-chain-a.json index dceb3f42289..de59b2e8da1 100644 --- a/.github/compatibility-test-matrices/release-v4.2.x/incentivized-transfer-chain-a.json +++ b/.github/compatibility-test-matrices/release-v4.2.x/incentivized-transfer-chain-a.json @@ -1,6 +1,6 @@ { "chain-a": ["release-v4.2.x"], - "chain-b": ["release-v4.2.x", "v5.1.0", "v5.0.1", "v4.1.1"], + "chain-b": ["release-v4.2.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.1.1"], "entrypoint": ["TestIncentivizedTransferTestSuite"], "test": [ "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", diff --git a/.github/compatibility-test-matrices/release-v4.2.x/incentivized-transfer-chain-b.json b/.github/compatibility-test-matrices/release-v4.2.x/incentivized-transfer-chain-b.json index 49a78be62e4..ae6e6c1c1d6 100644 --- a/.github/compatibility-test-matrices/release-v4.2.x/incentivized-transfer-chain-b.json +++ b/.github/compatibility-test-matrices/release-v4.2.x/incentivized-transfer-chain-b.json @@ -1,5 +1,5 @@ { - "chain-a": ["release-v4.2.x", "v5.1.0", "v5.0.1", "v4.1.1"], + "chain-a": ["release-v4.2.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.1.1"], "chain-b": ["release-v4.2.x"], "entrypoint": ["TestIncentivizedTransferTestSuite"], "test": [ diff --git a/.github/compatibility-test-matrices/release-v4.2.x/transfer-chain-a.json b/.github/compatibility-test-matrices/release-v4.2.x/transfer-chain-a.json index 260bc091444..1293f55b29d 100644 --- a/.github/compatibility-test-matrices/release-v4.2.x/transfer-chain-a.json +++ b/.github/compatibility-test-matrices/release-v4.2.x/transfer-chain-a.json @@ -1,6 +1,6 @@ { "chain-a": ["release-v4.2.x"], - "chain-b": ["release-v4.2.x", "v5.1.0", "v5.0.1", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-b": ["release-v4.2.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "entrypoint": ["TestTransferTestSuite"], "test": [ "TestMsgTransfer_Succeeds_Nonincentivized", diff --git a/.github/compatibility-test-matrices/release-v4.2.x/transfer-chain-b.json b/.github/compatibility-test-matrices/release-v4.2.x/transfer-chain-b.json index 69193d2fdae..97ec2a6a582 100644 --- a/.github/compatibility-test-matrices/release-v4.2.x/transfer-chain-b.json +++ b/.github/compatibility-test-matrices/release-v4.2.x/transfer-chain-b.json @@ -1,5 +1,5 @@ { - "chain-a": ["release-v4.2.x", "v5.1.0", "v5.0.1", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-a": ["release-v4.2.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "chain-b": ["release-v4.2.x"], "entrypoint": ["TestTransferTestSuite"], "test": [ diff --git a/.github/compatibility-test-matrices/release-v5.0.x/client-chain-a.json b/.github/compatibility-test-matrices/release-v5.0.x/client-chain-a.json index d75ee75115a..52650b7a003 100644 --- a/.github/compatibility-test-matrices/release-v5.0.x/client-chain-a.json +++ b/.github/compatibility-test-matrices/release-v5.0.x/client-chain-a.json @@ -1,6 +1,6 @@ { "chain-a": ["release-v5.0.x"], - "chain-b": ["release-v5.0.x", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-b": ["release-v5.0.x", "v6.0.0", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "entrypoint": ["TestClientTestSuite"], "test": [ "TestClientUpdateProposal_Succeeds" diff --git a/.github/compatibility-test-matrices/release-v5.0.x/client-chain-b.json b/.github/compatibility-test-matrices/release-v5.0.x/client-chain-b.json index 0af707d78b6..39f28546480 100644 --- a/.github/compatibility-test-matrices/release-v5.0.x/client-chain-b.json +++ b/.github/compatibility-test-matrices/release-v5.0.x/client-chain-b.json @@ -1,5 +1,5 @@ { - "chain-a": ["release-v5.0.x", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-a": ["release-v5.0.x", "v6.0.0", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "chain-b": ["release-v5.0.x"], "entrypoint": ["TestClientTestSuite"], "test": [ diff --git a/.github/compatibility-test-matrices/release-v5.0.x/connection-chain-a.json b/.github/compatibility-test-matrices/release-v5.0.x/connection-chain-a.json index 1090f5ef391..47e3170d3be 100644 --- a/.github/compatibility-test-matrices/release-v5.0.x/connection-chain-a.json +++ b/.github/compatibility-test-matrices/release-v5.0.x/connection-chain-a.json @@ -1,6 +1,6 @@ { "chain-a": ["release-v5.0.x"], - "chain-b": ["release-v5.0.x", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-b": ["release-v5.0.x", "v6.0.0", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "entrypoint": ["TestConnectionTestSuite"], "test": [ "TestMaxExpectedTimePerBlockParam" diff --git a/.github/compatibility-test-matrices/release-v5.0.x/connection-chain-b.json b/.github/compatibility-test-matrices/release-v5.0.x/connection-chain-b.json index dcfb168e0ba..9547569730d 100644 --- a/.github/compatibility-test-matrices/release-v5.0.x/connection-chain-b.json +++ b/.github/compatibility-test-matrices/release-v5.0.x/connection-chain-b.json @@ -1,5 +1,5 @@ { - "chain-a": ["release-v5.0.x", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-a": ["release-v5.0.x", "v6.0.0", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "chain-b": ["release-v5.0.x"], "entrypoint": ["TestConnectionTestSuite"], "test": [ diff --git a/.github/compatibility-test-matrices/release-v5.0.x/incentivized-transfer-chain-a.json b/.github/compatibility-test-matrices/release-v5.0.x/incentivized-transfer-chain-a.json index bb910d4f907..f24ef375ef1 100644 --- a/.github/compatibility-test-matrices/release-v5.0.x/incentivized-transfer-chain-a.json +++ b/.github/compatibility-test-matrices/release-v5.0.x/incentivized-transfer-chain-a.json @@ -1,6 +1,6 @@ { "chain-a": ["release-v5.0.x"], - "chain-b": ["release-v5.0.x", "v5.1.0", "v4.2.0", "v4.1.1"], + "chain-b": ["release-v5.0.x", "v6.0.0", "v5.1.0", "v4.2.0", "v4.1.1"], "entrypoint": ["TestIncentivizedTransferTestSuite"], "test": [ "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", diff --git a/.github/compatibility-test-matrices/release-v5.0.x/incentivized-transfer-chain-b.json b/.github/compatibility-test-matrices/release-v5.0.x/incentivized-transfer-chain-b.json index 70551e90d66..66708272426 100644 --- a/.github/compatibility-test-matrices/release-v5.0.x/incentivized-transfer-chain-b.json +++ b/.github/compatibility-test-matrices/release-v5.0.x/incentivized-transfer-chain-b.json @@ -1,5 +1,5 @@ { - "chain-a": ["release-v5.0.x", "v5.1.0", "v4.2.0", "v4.1.1"], + "chain-a": ["release-v5.0.x", "v6.0.0", "v5.1.0", "v4.2.0", "v4.1.1"], "chain-b": ["release-v5.0.x"], "entrypoint": ["TestIncentivizedTransferTestSuite"], "test": [ diff --git a/.github/compatibility-test-matrices/release-v5.0.x/transfer-chain-a.json b/.github/compatibility-test-matrices/release-v5.0.x/transfer-chain-a.json index 41d37591fd3..3454b8e7c5a 100644 --- a/.github/compatibility-test-matrices/release-v5.0.x/transfer-chain-a.json +++ b/.github/compatibility-test-matrices/release-v5.0.x/transfer-chain-a.json @@ -1,6 +1,6 @@ { "chain-a": ["release-v5.0.x"], - "chain-b": ["release-v5.0.x", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-b": ["release-v5.0.x", "v6.0.0", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "entrypoint": ["TestTransferTestSuite"], "test": [ "TestMsgTransfer_Succeeds_Nonincentivized", diff --git a/.github/compatibility-test-matrices/release-v5.0.x/transfer-chain-b.json b/.github/compatibility-test-matrices/release-v5.0.x/transfer-chain-b.json index 3bcbb0a49bc..1e28e2a54ab 100644 --- a/.github/compatibility-test-matrices/release-v5.0.x/transfer-chain-b.json +++ b/.github/compatibility-test-matrices/release-v5.0.x/transfer-chain-b.json @@ -1,5 +1,5 @@ { - "chain-a": ["release-v5.0.x", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-a": ["release-v5.0.x", "v6.0.0", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "chain-b": ["release-v5.0.x"], "entrypoint": ["TestTransferTestSuite"], "test": [ diff --git a/.github/compatibility-test-matrices/release-v5.1.x/incentivized-transfer-chain-a.json b/.github/compatibility-test-matrices/release-v5.1.x/incentivized-transfer-chain-a.json index 62a9663804d..a17abd2c4d5 100644 --- a/.github/compatibility-test-matrices/release-v5.1.x/incentivized-transfer-chain-a.json +++ b/.github/compatibility-test-matrices/release-v5.1.x/incentivized-transfer-chain-a.json @@ -1,6 +1,6 @@ { "chain-a": ["release-v5.1.x"], - "chain-b": ["release-v5.1.x", "v5.0.1", "v4.2.0", "v4.1.1"], + "chain-b": ["release-v5.1.x", "v6.0.0", "v5.0.1", "v4.2.0", "v4.1.1"], "entrypoint": ["TestIncentivizedTransferTestSuite"], "test": [ "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", diff --git a/.github/compatibility-test-matrices/release-v5.1.x/incentivized-transfer-chain-b.json b/.github/compatibility-test-matrices/release-v5.1.x/incentivized-transfer-chain-b.json index 0b47967d388..e1289a6f1f4 100644 --- a/.github/compatibility-test-matrices/release-v5.1.x/incentivized-transfer-chain-b.json +++ b/.github/compatibility-test-matrices/release-v5.1.x/incentivized-transfer-chain-b.json @@ -1,5 +1,5 @@ { - "chain-a": ["release-v5.1.x", "v5.0.1", "v4.2.0", "v4.1.1"], + "chain-a": ["release-v5.1.x", "v6.0.0", "v5.0.1", "v4.2.0", "v4.1.1"], "chain-b": ["release-v5.1.x"], "entrypoint": ["TestIncentivizedTransferTestSuite"], "test": [ diff --git a/.github/compatibility-test-matrices/release-v5.1.x/transfer-chain-a.json b/.github/compatibility-test-matrices/release-v5.1.x/transfer-chain-a.json index d1e0b88cc62..ef4a7297cd3 100644 --- a/.github/compatibility-test-matrices/release-v5.1.x/transfer-chain-a.json +++ b/.github/compatibility-test-matrices/release-v5.1.x/transfer-chain-a.json @@ -1,6 +1,6 @@ { "chain-a": ["release-v5.1.x"], - "chain-b": ["release-v5.1.x", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-b": ["release-v5.1.x", "v6.0.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "entrypoint": ["TestTransferTestSuite"], "test": [ "TestMsgTransfer_Succeeds_Nonincentivized", diff --git a/.github/compatibility-test-matrices/release-v5.1.x/transfer-chain-b.json b/.github/compatibility-test-matrices/release-v5.1.x/transfer-chain-b.json index 5582e6d300f..5da883292dd 100644 --- a/.github/compatibility-test-matrices/release-v5.1.x/transfer-chain-b.json +++ b/.github/compatibility-test-matrices/release-v5.1.x/transfer-chain-b.json @@ -1,5 +1,5 @@ { - "chain-a": ["release-v5.1.x", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-a": ["release-v5.1.x", "v6.0.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "chain-b": ["release-v5.1.x"], "entrypoint": ["TestTransferTestSuite"], "test": [ diff --git a/.github/workflows/e2e-manual-icad.yaml b/.github/workflows/e2e-manual-icad.yaml index 3700f943f89..9174f8f5722 100644 --- a/.github/workflows/e2e-manual-icad.yaml +++ b/.github/workflows/e2e-manual-icad.yaml @@ -25,7 +25,7 @@ on: default: master options: - master - - v0.4.1 + - v0.4.2 - v0.3.5 - v0.2.5 - v0.1.7 @@ -36,7 +36,7 @@ on: type: choice options: - master - - v0.4.1 + - v0.4.2 - v0.3.5 - v0.2.5 - v0.1.7 diff --git a/.github/workflows/e2e-manual-simd.yaml b/.github/workflows/e2e-manual-simd.yaml index 6218eb2dc0f..76bf85c6e2d 100644 --- a/.github/workflows/e2e-manual-simd.yaml +++ b/.github/workflows/e2e-manual-simd.yaml @@ -29,7 +29,7 @@ on: default: main options: - main - - v6.0.0-beta1 + - v6.0.0 - v5.1.0 - v5.0.1 - v4.2.0 @@ -39,13 +39,13 @@ on: - v2.5.0 - v2.4.2 chain-b-tag: - default: v6.0.0-beta1 + default: v6.0.0 description: 'The tag to use for chain B' required: true type: choice options: - main - - v6.0.0-beta1 + - v6.0.0 - v5.1.0 - v5.0.1 - v4.2.0 diff --git a/.github/workflows/e2e-upgrade.yaml b/.github/workflows/e2e-upgrade.yaml index 37c886ece31..adefcd0a264 100644 --- a/.github/workflows/e2e-upgrade.yaml +++ b/.github/workflows/e2e-upgrade.yaml @@ -18,7 +18,7 @@ jobs: chain-binary: icad chain-a-tag: v0.3.5 chain-b-tag: v0.3.5 - chain-upgrade-tag: v0.4.0 + chain-upgrade-tag: v0.4.2 steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index 47a484e5a3b..9742abb77ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,17 +38,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Dependencies -* [\#2868](https://github.com/cosmos/ibc-go/pull/2868) Bump ICS 23 to v0.9.0. -* [\#2784](https://github.com/cosmos/ibc-go/pull/2784) Bump Cosmos SDK to v0.46.6 and Tendermint to v0.34.23. - ### API Breaking * (core) [\#2897](https://github.com/cosmos/ibc-go/pull/2897) Remove legacy migrations required for upgrading from Stargate release line to ibc-go >= v1.x.x. * (core/02-client) [\#2856](https://github.com/cosmos/ibc-go/pull/2856) Rename `IterateClients` to `IterateClientStates`. The function now takes a prefix argument which may be used for prefix iteration over the client store. -* (apps/27-interchain-accounts) [\#2607](https://github.com/cosmos/ibc-go/pull/2607) `SerializeCosmosTx` now takes in a `[]proto.Message` instead of `[]sdk.Msg`. -* (apps/transfer) [\#2446](https://github.com/cosmos/ibc-go/pull/2446) Remove `SendTransfer` function in favor of a private `sendTransfer` function. All IBC transfers must be initiated with `MsgTransfer`. -* (apps/29-fee) [\#2395](https://github.com/cosmos/ibc-go/pull/2395) Remove param space from ics29 NewKeeper function. The field was unused. -* (apps/27-interchain-accounts) [\#2133](https://github.com/cosmos/ibc-go/pull/2133) Generates genesis protos in a separate directory to avoid circular import errors. The protobuf package name has changed for the genesis types. * (light-clients/tendermint)[\#1768](https://github.com/cosmos/ibc-go/pull/1768) Removed `AllowUpdateAfterExpiry`, `AllowUpdateAfterMisbehaviour` booleans as they are deprecated (see ADR026) * (06-solomachine) [\#1679](https://github.com/cosmos/ibc-go/pull/1679) Remove `types` sub-package from `06-solomachine` lightclient directory. * (07-tendermint) [\#1677](https://github.com/cosmos/ibc-go/pull/1677) Remove `types` sub-package from `07-tendermint` lightclient directory. @@ -63,17 +56,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (06-solomachine)[\#1906](https://github.com/cosmos/ibc-go/pull/1906/files) Removed `AllowUpdateAfterProposal` boolean as it has been deprecated (see 01_concepts of the solo machine spec for more details). * (07-tendermint) [\#1896](https://github.com/cosmos/ibc-go/pull/1896) Remove error return from `IterateConsensusStateAscending` in `07-tendermint`. * (apps/27-interchain-accounts) [\#2638](https://github.com/cosmos/ibc-go/pull/2638) Interchain accounts host and controller Keepers now expects a keeper which fulfills the expected `exported.ScopedKeeper` interface for the capability keeper. -* (transfer) [\#2638](https://github.com/cosmos/ibc-go/pull/2638) Transfer Keeper now expects a keeper which fulfills the expected `exported.ScopedKeeper` interface for the capability keeper. -* (05-port) [\#2638](https://github.com/cosmos/ibc-go/pull/2638) Port Keeper now expects a keeper which fulfills the expected `exported.ScopedKeeper` interface for the capability keeper. -* (04-channel) [\#2638](https://github.com/cosmos/ibc-go/pull/2638) Channel Keeper now expects a keeper which fulfills the expected `exported.ScopedKeeper` interface for the capability keeper. -* (core/04-channel)[\#1703](https://github.com/cosmos/ibc-go/pull/1703) Update `SendPacket` API to take in necessary arguments and construct rest of packet rather than taking in entire packet. The generated packet sequence is returned by the `SendPacket` function. -* (modules/apps/27-interchain-accounts) [\#2433](https://github.com/cosmos/ibc-go/pull/2450) Renamed icatypes.PortPrefix to icatypes.ControllerPortPrefix & icatypes.PortID to icatypes.HostPortID -* (testing) [\#2567](https://github.com/cosmos/ibc-go/pull/2567) Modify `SendPacket` API of `Endpoint` to match the API of `SendPacket` in 04-channel. * (06-solomachine) [\#2761](https://github.com/cosmos/ibc-go/pull/2761) Removed deprecated `ClientId` field from `Misbehaviour` and `allow_update_after_proposal` field from `ClientState`. ### State Machine Breaking -* (light-clients/07-tendermint) [\#2555](https://github.com/cosmos/ibc-go/pull/2555) Forbid negative values for `TrustingPeriod`, `UnbondingPeriod` and `MaxClockDrift` (as specified in ICS-07). * (06-solomachine) [\#2744](https://github.com/cosmos/ibc-go/pull/2744) `Misbehaviour.ValidateBasic()` now only enforces that signature data does not match when the signature paths are different. * (06-solomachine) [\#2748](https://github.com/cosmos/ibc-go/pull/2748) Adding sentinel value for header path in 06-solomachine. @@ -97,24 +83,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (modules/core/02-client) [\#1741](https://github.com/cosmos/ibc-go/pull/1741) Emitting a new `upgrade_chain` event upon setting upgrade consensus state. * (client) [\#724](https://github.com/cosmos/ibc-go/pull/724) `IsRevisionFormat` and `IsClientIDFormat` have been updated to disallow newlines before the dash used to separate the chainID and revision number, and the client type and client sequence. * (02-client/cli) [\#897](https://github.com/cosmos/ibc-go/pull/897) Remove `GetClientID()` from `Misbehaviour` interface. Submit client misbehaviour cli command requires an explicit client id now. -* (06-solomachine) [\#1972](https://github.com/cosmos/ibc-go/pull/1972) Solo machine implementation of `ZeroCustomFields` fn now panics as the fn is only used for upgrades which solo machine does not support. -* (apps/27-interchain-accounts) [\#2102](https://github.com/cosmos/ibc-go/pull/2102) ICS27 controller middleware now supports a nil underlying application. This allows chains to make use of interchain accounts with existing auth mechanisms such as x/group and x/gov. -* (apps/27-interchain-accounts) [\#2146](https://github.com/cosmos/ibc-go/pull/2146) ICS27 controller now claims the channel capability passed via ibc core, and passes `nil` to the underlying app callback. The channel capability arg in `SendTx` is now ignored and looked up internally. -* (apps/27-interchain-accounts) [\#2134](https://github.com/cosmos/ibc-go/pull/2134) Adding upgrade handler to ICS27 `controller` submodule for migration of channel capabilities. This upgrade handler migrates ownership of channel capabilities from the underlying application to the ICS27 `controller` submodule. -* (apps/27-interchain-accounts) [\#2157](https://github.com/cosmos/ibc-go/pull/2157) Adding `IsMiddlewareEnabled` functionality to enforce calls to ICS27 msg server to *not* route to the underlying application. -* (apps/27-interchain-accounts) [\#2177](https://github.com/cosmos/ibc-go/pull/2177) Adding `IsMiddlewareEnabled` flag to interchain accounts `ActiveChannel` genesis type. -* (apps/27-interchain-accounts) [\#2140](https://github.com/cosmos/ibc-go/pull/2140) Adding migration handler to ICS27 `controller` submodule to assert ownership of channel capabilities and set middleware enabled flag for existing channels. The ICS27 module consensus version has been bumped from 1 to 2. -* (core/04-channel) [\#2304](https://github.com/cosmos/ibc-go/pull/2304) Adding `GetAllChannelsWithPortPrefix` function which filters channels based on a provided port prefix. -* (apps/27-interchain-accounts) [\#2290](https://github.com/cosmos/ibc-go/pull/2290) Changed `DefaultParams` function in `host` submodule to allow all messages by default. Defined a constant named `AllowAllHostMsgs` for `host` module to keep wildcard "*" string which allows all messages. -* (apps/27-interchain-accounts) [\#2248](https://github.com/cosmos/ibc-go/pull/2248) Adding call to underlying app in `OnChanCloseConfirm` callback of the controller submodule and adding relevant unit tests. -* (apps/27-interchain-accounts) [\#2251](https://github.com/cosmos/ibc-go/pull/2251) Adding `msgServer` struct to controller submodule that embeds the `Keeper` struct. +* (06-solomachine) [\#1972](https://github.com/cosmos/ibc-go/pull/1972) Solo machine implementation of `ZeroCustomFields` fn now panics as the fn is only used for upgrades which solo machine does not support. * (light-clients/06-solomachine) Moving `verifyMisbehaviour` function from update.go to misbehaviour_handle.go. -* (apps/27-interchain-accounts) [\#2297](https://github.com/cosmos/ibc-go/pull/2297) Adding cli command to generate ICS27 packet data. -* (modules/core/keeper) [\#1728](https://github.com/cosmos/ibc-go/pull/2399) Updated channel callback errors to include portID & channelID for better identification of errors. * [\#2434](https://github.com/cosmos/ibc-go/pull/2478) Removed all `TypeMsg` constants * (modules/core/exported) [#1689] (https://github.com/cosmos/ibc-go/pull/2539) Removing `GetVersions` from `ConnectionI` interface. -* (core/03-connection) [\#2745](https://github.com/cosmos/ibc-go/pull/2745) Adding `ConnectionParams` grpc query and CLI to 03-connection. -* (apps/29-fee) [\#2786](https://github.com/cosmos/ibc-go/pull/2786) Save gas by checking key existence with `KVStore`'s `Has` method. ### Features @@ -125,8 +97,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (light-clients/07-tendermint) [\#2825](https://github.com/cosmos/ibc-go/pull/2825) Add `AppModuleBasic` for the 07-tendermint client and remove tendermint type registration from core IBC. Chains must register the `AppModuleBasic` of light clients. * (light-clients/07-tendermint) [\#2800](https://github.com/cosmos/ibc-go/pull/2800) Add optional in-place store migration function to prune all expired tendermint consensus states. * (core/24-host) [\#2820](https://github.com/cosmos/ibc-go/pull/2820) Add `MustParseClientStatePath` which parses the clientID from a client state key path. -* (apps/27-interchain-accounts) [\#2147](https://github.com/cosmos/ibc-go/pull/2147) Adding a `SubmitTx` gRPC endpoint for the ICS27 Controller module which allows owners of interchain accounts to submit transactions. This replaces the previously existing need for authentication modules to implement this standard functionality. -* (testing/simapp) [\#2190](https://github.com/cosmos/ibc-go/pull/2190) Adding the new `x/group` cosmos-sdk module to simapp. * (testing/simapp) [\#2842](https://github.com/cosmos/ibc-go/pull/2842) Adding the new upgrade handler for v6 -> v7 to simapp which prunes expired Tendermint consensus states. ### Bug Fixes @@ -134,9 +104,66 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (light-clients/solomachine) [#1839](https://github.com/cosmos/ibc-go/issues/1839) Fixed usage of the new diversifier in validation of changing diversifiers for the solo machine. The current diversifier must sign over the new diversifier. * (light-clients/07-tendermint) [\#1674](https://github.com/cosmos/ibc-go/pull/1674) Submitted ClientState is zeroed out before checking the proof in order to prevent the proposal from containing information governance is not actually voting on. * (modules/core/02-client)[\#1676](https://github.com/cosmos/ibc-go/pull/1676) ClientState must be zeroed out for `UpgradeProposals` to pass validation. This prevents a proposal containing information governance is not actually voting on. +* (core/02-client) [\#2510](https://github.com/cosmos/ibc-go/pull/2510) Fix client ID validation regex to conform closer to spec. + +## [v6.0.0](https://github.com/cosmos/ibc-go/releases/tag/v4.0.0) - 2022-12-09 + +### Dependencies + +* [\#2868](https://github.com/cosmos/ibc-go/pull/2868) Bump ICS 23 to v0.9.0. +* [\#2458](https://github.com/cosmos/ibc-go/pull/2458) Bump Cosmos SDK to v0.46.2 +* [\#2784](https://github.com/cosmos/ibc-go/pull/2784) Bump Cosmos SDK to v0.46.6 and Tendermint to v0.34.23. + +### API Breaking + +* (apps/27-interchain-accounts) [\#2607](https://github.com/cosmos/ibc-go/pull/2607) `SerializeCosmosTx` now takes in a `[]proto.Message` instead of `[]sdk.Msg`. +* (apps/transfer) [\#2446](https://github.com/cosmos/ibc-go/pull/2446) Remove `SendTransfer` function in favor of a private `sendTransfer` function. All IBC transfers must be initiated with `MsgTransfer`. +* (apps/29-fee) [\#2395](https://github.com/cosmos/ibc-go/pull/2395) Remove param space from ics29 NewKeeper function. The field was unused. +* (apps/27-interchain-accounts) [\#2133](https://github.com/cosmos/ibc-go/pull/2133) Generates genesis protos in a separate directory to avoid circular import errors. The protobuf package name has changed for the genesis types. +* (apps/27-interchain-accounts) [\#2638](https://github.com/cosmos/ibc-go/pull/2638) Interchain accounts host and controller Keepers now expects a keeper which fulfills the expected `exported.ScopedKeeper` interface for the capability keeper. +* (transfer) [\#2638](https://github.com/cosmos/ibc-go/pull/2638) Transfer Keeper now expects a keeper which fulfills the expected `exported.ScopedKeeper` interface for the capability keeper. +* (05-port) [\#2638](https://github.com/cosmos/ibc-go/pull/2638) Port Keeper now expects a keeper which fulfills the expected `exported.ScopedKeeper` interface for the capability keeper. +* (04-channel) [\#2638](https://github.com/cosmos/ibc-go/pull/2638) Channel Keeper now expects a keeper which fulfills the expected `exported.ScopedKeeper` interface for the capability keeper. +* (core/04-channel)[\#1703](https://github.com/cosmos/ibc-go/pull/1703) Update `SendPacket` API to take in necessary arguments and construct rest of packet rather than taking in entire packet. The generated packet sequence is returned by the `SendPacket` function. +* (modules/apps/27-interchain-accounts) [\#2433](https://github.com/cosmos/ibc-go/pull/2450) Renamed icatypes.PortPrefix to icatypes.ControllerPortPrefix & icatypes.PortID to icatypes.HostPortID +* (testing) [\#2567](https://github.com/cosmos/ibc-go/pull/2567) Modify `SendPacket` API of `Endpoint` to match the API of `SendPacket` in 04-channel. + +### State Machine Breaking + +* (apps/transfer) [\#2651](https://github.com/cosmos/ibc-go/pull/2651) Introduce `mustProtoMarshalJSON` for ics20 packet data marshalling which will skip emission (marshalling) of the memo field if unpopulated (empty). +* (27-interchain-accounts) [\#2580](https://github.com/cosmos/ibc-go/issues/2580) Removing port prefix requirement from the ICA host channel handshake +* (transfer) [\#2377](https://github.com/cosmos/ibc-go/pull/2377) Adding `sequence` to `MsgTransferResponse`. +* (light-clients/07-tendermint) [\#2555](https://github.com/cosmos/ibc-go/pull/2555) Forbid negative values for `TrustingPeriod`, `UnbondingPeriod` and `MaxClockDrift` (as specified in ICS-07). + +### Improvements + +* (apps/27-interchain-accounts) [\#2134](https://github.com/cosmos/ibc-go/pull/2134) Adding upgrade handler to ICS27 `controller` submodule for migration of channel capabilities. This upgrade handler migrates ownership of channel capabilities from the underlying application to the ICS27 `controller` submodule. +* (apps/27-interchain-accounts) [\#2102](https://github.com/cosmos/ibc-go/pull/2102) ICS27 controller middleware now supports a nil underlying application. This allows chains to make use of interchain accounts with existing auth mechanisms such as x/group and x/gov. +* (apps/27-interchain-accounts) [\#2157](https://github.com/cosmos/ibc-go/pull/2157) Adding `IsMiddlewareEnabled` functionality to enforce calls to ICS27 msg server to *not* route to the underlying application. +* (apps/27-interchain-accounts) [\#2146](https://github.com/cosmos/ibc-go/pull/2146) ICS27 controller now claims the channel capability passed via ibc core, and passes `nil` to the underlying app callback. The channel capability arg in `SendTx` is now ignored and looked up internally. +* (apps/27-interchain-accounts) [\#2177](https://github.com/cosmos/ibc-go/pull/2177) Adding `IsMiddlewareEnabled` flag to interchain accounts `ActiveChannel` genesis type. +* (apps/27-interchain-accounts) [\#2140](https://github.com/cosmos/ibc-go/pull/2140) Adding migration handler to ICS27 `controller` submodule to assert ownership of channel capabilities and set middleware enabled flag for existing channels. The ICS27 module consensus version has been bumped from 1 to 2. +* (core/04-channel) [\#2304](https://github.com/cosmos/ibc-go/pull/2304) Adding `GetAllChannelsWithPortPrefix` function which filters channels based on a provided port prefix. +* (apps/27-interchain-accounts) [\#2248](https://github.com/cosmos/ibc-go/pull/2248) Adding call to underlying app in `OnChanCloseConfirm` callback of the controller submodule and adding relevant unit tests. +* (apps/27-interchain-accounts) [\#2251](https://github.com/cosmos/ibc-go/pull/2251) Adding `msgServer` struct to controller submodule that embeds the `Keeper` struct. +* (apps/27-interchain-accounts) [\#2290](https://github.com/cosmos/ibc-go/pull/2290) Changed `DefaultParams` function in `host` submodule to allow all messages by default. Defined a constant named `AllowAllHostMsgs` for `host` module to keep wildcard "*" string which allows all messages. +* (apps/27-interchain-accounts) [\#2297](https://github.com/cosmos/ibc-go/pull/2297) Adding cli command to generate ICS27 packet data. +* (modules/core/keeper) [\#1728](https://github.com/cosmos/ibc-go/pull/2399) Updated channel callback errors to include portID & channelID for better identification of errors. +* (testing) [\#2657](https://github.com/cosmos/ibc-go/pull/2657) Carry `ProposerAddress` through commited blocks. Allow `DefaultGenTxGas` to be modified. +* (core/03-connection) [\#2745](https://github.com/cosmos/ibc-go/pull/2745) Adding `ConnectionParams` grpc query and CLI to 03-connection. +* (apps/29-fee) [\#2786](https://github.com/cosmos/ibc-go/pull/2786) Save gas by checking key existence with `KVStore`'s `Has` method. + +### Features + +* (apps/27-interchain-accounts) [\#2147](https://github.com/cosmos/ibc-go/pull/2147) Adding a `SubmitTx` gRPC endpoint for the ICS27 Controller module which allows owners of interchain accounts to submit transactions. This replaces the previously existing need for authentication modules to implement this standard functionality. +* (testing/simapp) [\#2190](https://github.com/cosmos/ibc-go/pull/2190) Adding the new `x/group` cosmos-sdk module to simapp. +* (apps/transfer) [\#2595](https://github.com/cosmos/ibc-go/pull/2595) Adding optional memo field to `FungibleTokenPacketData` and `MsgTransfer`. + +### Bug Fixes + * (modules/core/keeper) [\#2403](https://github.com/cosmos/ibc-go/pull/2403) Added a function in keeper to cater for blank pointers. +* (apps/transfer) [\#2679](https://github.com/cosmos/ibc-go/pull/2679) Check `x/bank` send enabled. * (modules/core/keeper) [\#2745](https://github.com/cosmos/ibc-go/pull/2745) Fix request wiring for `UpgradedConsensusState` in core query server. -* (core/02-client) [\#2510](https://github.com/cosmos/ibc-go/pull/2510) Fix client ID validation regex to conform closer to spec. ## [v5.1.0](https://github.com/cosmos/ibc-go/releases/tag/v5.1.0) - 2022-11-09 diff --git a/README.md b/README.md index 87eee687f91..22a649bb233 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ For the latest information on the progress of the work or the decisions made tha ## Releases -The release lines currently supported are v2, v3, v4 and v5. +The release lines currently supported are v2, v3, v4, v5 and v6. Please refer to the [Stable Release Policy section of RELEASES.md](https://github.com/cosmos/ibc-go/blob/main/RELEASES.md#stable-release-policy) for more details. diff --git a/RELEASES.md b/RELEASES.md index 61176c373d0..fb7c1693fca 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -73,6 +73,7 @@ Only the following major release series have a stable release status: |`v4.2.x`|August 12, 2023| |`v5.0.x`|September 28, 2023| |`v5.1.x`|September 28, 2023| +|`v6.0.x`|December 09, 2023| All missing minor release versions have been discontinued. @@ -111,6 +112,7 @@ Versions of Golang, Cosmos SDK and Tendermint used by ibc-go in the currently ac | 1.18 | v4.2.0 | v0.45.10 | v0.34.22 | | 1.18 | v5.0.1 | v0.46.3 | v0.34.22 | | 1.18 | v5.1.0 | v0.46.4 | v0.34.22 | +| 1.18 | v6.0.0 | v0.46.6 | v0.34.23 | ## Graphics diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 9c44dcecbb8..5ab94844910 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -148,6 +148,10 @@ module.exports = { label: "v5.1.0", key: "v5.1.0", }, + { + label: "v6.0.0", + key: "v6.0.0", + }, ], topbar: { banner: true, diff --git a/docs/apps/interchain-accounts/messages.md b/docs/apps/interchain-accounts/messages.md index 125a0203f8b..9424bbcf77f 100644 --- a/docs/apps/interchain-accounts/messages.md +++ b/docs/apps/interchain-accounts/messages.md @@ -23,7 +23,7 @@ This message is expected to fail if: This message will construct a new `MsgChannelOpenInit` on chain and route it to the core IBC message server to initiate the opening step of the channel handshake. -The controller submodule will generate a new port identifier and claim the associated port capability. The caller is expected to provide an appropriate application version string. For example, this may be an ICS-27 JSON encoded [`Metadata`](https://github.com/cosmos/ibc-go/blob/v6.0.0-rc0/proto/ibc/applications/interchain_accounts/v1/metadata.proto#L11) type or an ICS-29 JSON encoded [`Metadata`](https://github.com/cosmos/ibc-go/blob/v6.0.0-rc0/proto/ibc/applications/fee/v1/metadata.proto#L11) type with a nested application version. +The controller submodule will generate a new port identifier and claim the associated port capability. The caller is expected to provide an appropriate application version string. For example, this may be an ICS-27 JSON encoded [`Metadata`](https://github.com/cosmos/ibc-go/blob/v6.0.0/proto/ibc/applications/interchain_accounts/v1/metadata.proto#L11) type or an ICS-29 JSON encoded [`Metadata`](https://github.com/cosmos/ibc-go/blob/v6.0.0/proto/ibc/applications/fee/v1/metadata.proto#L11) type with a nested application version. If the `Version` string is omitted, the controller submodule will construct a default version string in the `OnChanOpenInit` handshake callback. ```go diff --git a/docs/ibc/light-clients/client-state.md b/docs/ibc/light-clients/client-state.md index cff2fd23f21..fa32e90fe8a 100644 --- a/docs/ibc/light-clients/client-state.md +++ b/docs/ibc/light-clients/client-state.md @@ -4,7 +4,7 @@ order: 2 # Implementing the `ClientState` interface -Learn how to implement the [`ClientState`](https://github.com/cosmos/ibc-go/blob/v6.0.0-rc1/modules/core/exported/client.go#L40) interface. +Learn how to implement the [`ClientState`](https://github.com/cosmos/ibc-go/blob/v6.0.0/modules/core/exported/client.go#L40) interface. ## `ClientType` method @@ -18,7 +18,7 @@ The format is created as follows: `ClientType-{N}` where `{N}` is the unique glo ## `Validate` method `Validate` should validate every client state field and should return an error if any value is invalid. The light client -implementor is in charge of determining which checks are required. See the [tendermint light client implementation](https://github.com/cosmos/ibc-go/blob/v6.0.0-rc1/modules/light-clients/07-tendermint/types/client_state.go#L101) +implementor is in charge of determining which checks are required. See the [tendermint light client implementation](https://github.com/cosmos/ibc-go/blob/v6.0.0/modules/light-clients/07-tendermint/types/client_state.go#L101) as a reference. ## `Status` method @@ -30,14 +30,14 @@ as a reference. - An `Expired` status indicates that a client is not allowed to be used. - An `Unknown` status indicates that there was an error in determining the status of a client. -All possible Status types can be found [here](https://github.com/cosmos/ibc-go/blob/v6.0.0-rc1/modules/core/exported/client.go#L26-L36). +All possible Status types can be found [here](https://github.com/cosmos/ibc-go/blob/v6.0.0/modules/core/exported/client.go#L26-L36). -This field is returned by the gRPC [QueryClientStatusResponse](https://github.com/cosmos/ibc-go/blob/v6.0.0-rc1/modules/core/02-client/types/query.pb.go#L665) endpoint. +This field is returned by the gRPC [QueryClientStatusResponse](https://github.com/cosmos/ibc-go/blob/v6.0.0/modules/core/02-client/types/query.pb.go#L665) endpoint. ## `ZeroCustomFields` method `ZeroCustomFields` should return a copy of the light client with all client customizable fields with their zero value. It should not mutate the fields of the light client. -This method is used when [scheduling upgrades](https://github.com/cosmos/ibc-go/blob/v6.0.0-rc1/modules/core/02-client/keeper/proposal.go#L89). Upgrades are used to upgrade chain specific fields. +This method is used when [scheduling upgrades](https://github.com/cosmos/ibc-go/blob/v6.0.0/modules/core/02-client/keeper/proposal.go#L89). Upgrades are used to upgrade chain specific fields. In the tendermint case, this may be the chainID or the unbonding period. For more information about client upgrades see [the developer guide](../upgrades/developer-guide.md). diff --git a/docs/roadmap/roadmap.md b/docs/roadmap/roadmap.md index 4862e4db48c..b50052087ae 100644 --- a/docs/roadmap/roadmap.md +++ b/docs/roadmap/roadmap.md @@ -4,35 +4,12 @@ order: 1 # Roadmap ibc-go -_Lastest update: October 3, 2022_ +_Lastest update: December 9, 2022_ This document endeavours to inform the wider IBC community about plans and priorities for work on ibc-go by the team at Interchain GmbH. It is intended to broadly inform all users of ibc-go, including developers and operators of IBC, relayer, chain and wallet applications. This roadmap should be read as a high-level guide, rather than a commitment to schedules and deliverables. The degree of specificity is inversely proportional to the timeline. We will update this document periodically to reflect the status and plans. -## v6.0.0 - -### Interchain Accounts improvements - -These improvements tackle two shortcomings of the current implementation of ICS-27: - -- The lack of a default/standard underlying app (also known as _authentication module_), which created a bottleneck for chains looking to integrate the controller submodule. -- The need to separate application and authentication concerns. - -The changes will make integrating the controller chain functionality easier (see [ADR 009](../architecture/adr-009-v6-ics27-msgserver.md) for more information), and with the release of Cosmos SDK v0.46.x, they will enable any Cosmos SDK module (e.g `x/auth`, `x/gov`, `x/group`) to register interchain accounts and send transactions on their behalf. - -For more details about these changes, please read [this blog post](https://medium.com/the-interchain-foundation/ibc-go-v6-changes-to-interchain-accounts-and-how-it-impacts-your-chain-806c185300d7). - -### Other features/improvements - -- ICS-20: - - Adding a `metadata` field in `FungibleTokenPacketData` (implementation of [this](https://github.com/cosmos/ibc/pull/842) spec update). - - Adding the sequence number of the packet sent to `MsgTransferResponse`. -- 04-channel: - - Simplify `SendPacket` API (implementation of [this](https://github.com/cosmos/ibc/pull/731) spec update). - -Follow the progress with the [`v6.0.0` milestone](https://github.com/cosmos/ibc-go/milestone/35) or the [project board](https://github.com/orgs/cosmos/projects/7/views/23). - ## v7.0.0 ### 02-client refactor @@ -41,16 +18,18 @@ This refactor will make the development of light clients easier. The ibc-go impl Follow the progress with the [beta](https://github.com/cosmos/ibc-go/milestone/25) and [RC](https://github.com/cosmos/ibc-go/milestone/27) milestones or in the [project board](https://github.com/orgs/cosmos/projects/7/views/14). +### Upgrade SDK v0.47.x + +Follow the progress with the [milestone](https://github.com/cosmos/ibc-go/milestone/36). + +## v7.1.0 + ### Localhost connection This feature will add support for applications on a chain to communicate with applications on the same chain using the existing standard interface to communicate with applications on remote chains. For more details, see the design proposal and discussion [here](https://github.com/cosmos/ibc-go/discussions/2191). Issues need still to be created and will be added to the [`v7.0.0` milestone](https://github.com/cosmos/ibc-go/milestone/34). -### Upgrade SDK v0.47.x - -Follow the progress with the [milestone](https://github.com/cosmos/ibc-go/milestone/36). - ## v8.0.0 ### Channel upgradability diff --git a/docs/versions b/docs/versions index a4c1bdb35fc..74f0e97c8be 100644 --- a/docs/versions +++ b/docs/versions @@ -1,3 +1,4 @@ +release/v6.0.x v6.0.0 release/v5.1.x v5.1.0 release/v5.0.x v5.0.0 release/v4.2.x v4.2.0 diff --git a/e2e/go.mod b/e2e/go.mod index 55ad7e415f7..20971b2c6cb 100644 --- a/e2e/go.mod +++ b/e2e/go.mod @@ -6,7 +6,7 @@ replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alp require ( github.com/cosmos/cosmos-sdk v0.46.6 - github.com/cosmos/ibc-go/v6 v6.0.0-alpha1 + github.com/cosmos/ibc-go/v6 v6.0.0 github.com/cosmos/interchain-accounts v0.4.0 github.com/docker/docker v20.10.19+incompatible github.com/gogo/protobuf v1.3.3 @@ -148,6 +148,7 @@ require ( github.com/prometheus/procfs v0.8.0 // indirect github.com/rakyll/statik v0.1.7 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/regen-network/cosmos-proto v0.3.1 // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/rs/cors v1.8.2 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect diff --git a/e2e/go.sum b/e2e/go.sum index a2d5c3a3e94..8fb9660bc34 100644 --- a/e2e/go.sum +++ b/e2e/go.sum @@ -772,6 +772,7 @@ github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Ung github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/regen-network/cosmos-proto v0.3.1 h1:rV7iM4SSFAagvy8RiyhiACbWEGotmqzywPxOvwMdxcg= +github.com/regen-network/cosmos-proto v0.3.1/go.mod h1:jO0sVX6a1B36nmE8C9xBFXpNwWejXC7QqCOnH3O0+YM= github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= From bb08f5eda2948597316d45527b76ccee32ec6128 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Mon, 12 Dec 2022 15:27:30 +0000 Subject: [PATCH 31/44] docs: update integration docs to include light client registration. (#2905) --- docs/ibc/integration.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/ibc/integration.md b/docs/ibc/integration.md index 013cce0db58..76fcabf4e5e 100644 --- a/docs/ibc/integration.md +++ b/docs/ibc/integration.md @@ -28,7 +28,23 @@ The first step is to add the following modules to the `BasicManager`: `x/capabil and `x/ibc-transfer`. After that, we need to grant `Minter` and `Burner` permissions to the `ibc-transfer` `ModuleAccount` to mint and burn relayed tokens. +### Integrating Light Clients + +> Note that from v7 onwards, all light clients have to be explicitly registered in a chain's app.go and follow the steps listed below. + This is in contrast to earlier versions of ibc-go when 07-tendermint and 06-solomachine were added out of the box. + +All light clients must be registered with `module.BasicManager` in a chain's app.go file. + +The following code example shows how to register the existing `ibctm.AppModuleBasic{}` light client implementation. + ```go + +import ( + ... + ibctm "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint" + ... +) + // app.go var ( @@ -37,6 +53,9 @@ var ( capability.AppModuleBasic{}, ibc.AppModuleBasic{}, transfer.AppModuleBasic{}, // i.e ibc-transfer module + + // register light clients on IBC + ibctm.AppModuleBasic{}, ) // module account permissions From 2220957926cfaec8d82400ef75ca18da7af5fe4d Mon Sep 17 00:00:00 2001 From: Anmol Date: Mon, 12 Dec 2022 22:19:00 +0530 Subject: [PATCH 32/44] imp: Add `AssertEvents` which asserts events against expected event map. (#2829) --- CHANGELOG.md | 1 + .../apps/transfer/keeper/msg_server_test.go | 53 ++++++------------- testing/events.go | 33 ++++++++++++ 3 files changed, 51 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9742abb77ed..3a9da1f226a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -98,6 +98,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (light-clients/07-tendermint) [\#2800](https://github.com/cosmos/ibc-go/pull/2800) Add optional in-place store migration function to prune all expired tendermint consensus states. * (core/24-host) [\#2820](https://github.com/cosmos/ibc-go/pull/2820) Add `MustParseClientStatePath` which parses the clientID from a client state key path. * (testing/simapp) [\#2842](https://github.com/cosmos/ibc-go/pull/2842) Adding the new upgrade handler for v6 -> v7 to simapp which prunes expired Tendermint consensus states. +* (testing) [\#2829](https://github.com/cosmos/ibc-go/pull/2829) Add `AssertEvents` which asserts events against expected event map. ### Bug Fixes diff --git a/modules/apps/transfer/keeper/msg_server_test.go b/modules/apps/transfer/keeper/msg_server_test.go index 0fe65b4ebdc..e457d3894cb 100644 --- a/modules/apps/transfer/keeper/msg_server_test.go +++ b/modules/apps/transfer/keeper/msg_server_test.go @@ -3,42 +3,15 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + ibctesting "github.com/cosmos/ibc-go/v6/testing" "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" ) -func (suite *KeeperTestSuite) assertTransferEvents( - actualEvents sdk.Events, - coin sdk.Coin, - memo string, -) { - hasEvent := false - - expEvent := map[string]string{ - sdk.AttributeKeySender: suite.chainA.SenderAccount.GetAddress().String(), - types.AttributeKeyReceiver: suite.chainB.SenderAccount.GetAddress().String(), - types.AttributeKeyAmount: coin.Amount.String(), - types.AttributeKeyDenom: coin.Denom, - types.AttributeKeyMemo: memo, - } - - for _, event := range actualEvents { - if event.Type == types.EventTypeTransfer { - hasEvent = true - suite.Require().Len(event.Attributes, len(expEvent)) - for _, attr := range event.Attributes { - expValue, found := expEvent[string(attr.Key)] - suite.Require().True(found) - suite.Require().Equal(expValue, string(attr.Value)) - } - } - } - - suite.Require().True(hasEvent, "event: %s was not found in events", types.EventTypeTransfer) -} - func (suite *KeeperTestSuite) TestMsgTransfer() { - var msg *types.MsgTransfer + var ( + msg *types.MsgTransfer + ) testCases := []struct { name string @@ -127,18 +100,26 @@ func (suite *KeeperTestSuite) TestMsgTransfer() { ctx := suite.chainA.GetContext() res, err := suite.chainA.GetSimApp().TransferKeeper.Transfer(sdk.WrapSDKContext(ctx), msg) + // Verify events + events := ctx.EventManager().Events() + expEvents := ibctesting.EventsMap{ + "ibc_transfer": { + "sender": suite.chainA.SenderAccount.GetAddress().String(), + "receiver": suite.chainB.SenderAccount.GetAddress().String(), + "amount": coin.Amount.String(), + "denom": coin.Denom, + "memo": "memo", + }, + } + if tc.expPass { suite.Require().NoError(err) suite.Require().NotNil(res) suite.Require().NotEqual(res.Sequence, uint64(0)) - - events := ctx.EventManager().Events() - suite.assertTransferEvents(events, coin, "memo") + ibctesting.AssertEvents(&suite.Suite, expEvents, events) } else { suite.Require().Error(err) suite.Require().Nil(res) - - events := ctx.EventManager().Events() suite.Require().Len(events, 0) } }) diff --git a/testing/events.go b/testing/events.go index 46dfbcf987e..22f6b81aeda 100644 --- a/testing/events.go +++ b/testing/events.go @@ -5,12 +5,15 @@ import ( "strconv" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" connectiontypes "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types" ) +type EventsMap map[string]map[string]string + // ParseClientIDFromEvents parses events emitted from a MsgCreateClient and returns the // client identifier. func ParseClientIDFromEvents(events sdk.Events) (string, error) { @@ -129,3 +132,33 @@ func ParseAckFromEvents(events sdk.Events) ([]byte, error) { } return nil, fmt.Errorf("acknowledgement event attribute not found") } + +// AssertEvents asserts that expected events are present in the actual events. +// Expected map needs to be a subset of actual events to pass. +func AssertEvents( + suite *suite.Suite, + expected EventsMap, + actual sdk.Events, +) { + hasEvents := make(map[string]bool) + for eventType := range expected { + hasEvents[eventType] = false + } + + for _, event := range actual { + expEvent, eventFound := expected[event.Type] + if eventFound { + hasEvents[event.Type] = true + suite.Require().Len(event.Attributes, len(expEvent)) + for _, attr := range event.Attributes { + expValue, found := expEvent[string(attr.Key)] + suite.Require().True(found) + suite.Require().Equal(expValue, string(attr.Value)) + } + } + } + + for eventName, hasEvent := range hasEvents { + suite.Require().True(hasEvent, "event: %s was not found in events", eventName) + } +} From 6afa1b6fdb9e30303b8cb0e21bf65e8668e9ab6a Mon Sep 17 00:00:00 2001 From: Charly Date: Mon, 12 Dec 2022 20:19:10 +0100 Subject: [PATCH 33/44] e2e: adding e2e upgrade test for ibc-go v7 (#2902) * add test for automatic migration of solomachine clientstate version * add clientID generation functions * update godoc * update sprintf message --- .github/workflows/e2e-upgrade.yaml | 8 +- e2e/tests/upgrades/upgrade_test.go | 176 +++++++++++++++++++++++++++++ e2e/testsuite/grpc_query.go | 13 +++ e2e/testsuite/testsuite.go | 8 +- e2e/testvalues/values.go | 9 ++ 5 files changed, 209 insertions(+), 5 deletions(-) diff --git a/.github/workflows/e2e-upgrade.yaml b/.github/workflows/e2e-upgrade.yaml index adefcd0a264..9432b52a4b9 100644 --- a/.github/workflows/e2e-upgrade.yaml +++ b/.github/workflows/e2e-upgrade.yaml @@ -18,7 +18,13 @@ jobs: chain-binary: icad chain-a-tag: v0.3.5 chain-b-tag: v0.3.5 - chain-upgrade-tag: v0.4.2 + chain-upgrade-tag: v0.4.0 + - test: TestV6ToV7ChainUpgrade + chain-image: ghcr.io/cosmos/ibc-go-simd + chain-binary: simd + chain-a-tag: v6.0.0 + chain-b-tag: v6.0.0 + chain-upgrade-tag: v7.0.0 steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 diff --git a/e2e/tests/upgrades/upgrade_test.go b/e2e/tests/upgrades/upgrade_test.go index dc74d2047e4..faff471bb6c 100644 --- a/e2e/tests/upgrades/upgrade_test.go +++ b/e2e/tests/upgrades/upgrade_test.go @@ -24,8 +24,13 @@ import ( "github.com/cosmos/ibc-go/e2e/testvalues" controllertypes "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/controller/types" icatypes "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/types" + v7migrations "github.com/cosmos/ibc-go/v6/modules/core/02-client/migrations/v7" + clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v6/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v6/modules/light-clients/06-solomachine" ibctesting "github.com/cosmos/ibc-go/v6/testing" simappupgrades "github.com/cosmos/ibc-go/v6/testing/simapp/upgrades" + v7upgrades "github.com/cosmos/ibc-go/v6/testing/simapp/upgrades/v7" ) const ( @@ -70,6 +75,10 @@ func (s *UpgradeTestSuite) UpgradeChain(ctx context.Context, chain *cosmos.Cosmo err = chain.StartAllNodes(ctx) s.Require().NoError(err, "error starting upgraded node(s)") + // we are reinitializing the clients because we need to update the hostGRPCAddress after + // the upgrade and subsequent restarting of nodes + s.InitGRPCClients(chain) + timeoutCtx, timeoutCtxCancel = context.WithTimeout(ctx, time.Minute*2) defer timeoutCtxCancel() @@ -386,6 +395,160 @@ func (s *UpgradeTestSuite) TestV5ToV6ChainUpgrade() { }) } +// TestV6ToV7ChainUpgrade will test that an upgrade from a v6 ibc-go binary to a v7 ibc-go binary is successful +// and that the automatic migrations associated with the 02-client module are performed. Namely that the solo machine +// proto definition is migrated in state from the v2 to v3 definition. This is checked by creating a solo machine client +// before the upgrade and asserting that its TypeURL has been changed after the upgrade. The test also ensure packets +// can be sent before and after the upgrade without issue +func (s *UpgradeTestSuite) TestV6ToV7ChainUpgrade() { + t := s.T() + testCfg := testconfig.FromEnv() + + ctx := context.Background() + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx) + chainA, chainB := s.GetChains() + + var ( + chainADenom = chainA.Config().Denom + chainBIBCToken = testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) // IBC token sent to chainB + ) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.Bech32Address(chainA.Config().Bech32Prefix) + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.Bech32Address(chainB.Config().Bech32Prefix) + + // create second tendermint client + createClientOptions := ibc.CreateClientOptions{ + TrustingPeriod: ibctesting.TrustingPeriod.String(), + } + + s.SetupClients(ctx, relayer, createClientOptions) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("check that both tendermint clients are active", func(t *testing.T) { + status, err := s.QueryClientStatus(ctx, chainA, testvalues.TendermintClientID(0)) + s.Require().NoError(err) + s.Require().Equal(exported.Active.String(), status) + + status, err = s.QueryClientStatus(ctx, chainA, testvalues.TendermintClientID(1)) + s.Require().NoError(err) + s.Require().Equal(exported.Active.String(), status) + }) + + // create solo machine client using the solomachine implementation from ibctesting + // TODO: the solomachine clientID should be updated when after fix of this issue: https://github.com/cosmos/ibc-go/issues/2907 + solo := ibctesting.NewSolomachine(t, testsuite.Codec(), "solomachine", "testing", 1) + + legacyConsensusState := &v7migrations.ConsensusState{ + PublicKey: solo.ConsensusState().PublicKey, + Diversifier: solo.ConsensusState().Diversifier, + Timestamp: solo.ConsensusState().Timestamp, + } + + legacyClientState := &v7migrations.ClientState{ + Sequence: solo.ClientState().Sequence, + IsFrozen: solo.ClientState().IsFrozen, + ConsensusState: legacyConsensusState, + AllowUpdateAfterProposal: true, + } + + msgCreateSoloMachineClient, err := clienttypes.NewMsgCreateClient(legacyClientState, legacyConsensusState, chainAAddress) + s.Require().NoError(err) + + resp, err := s.BroadcastMessages( + ctx, + chainA, + chainAWallet, + msgCreateSoloMachineClient, + ) + + s.AssertValidTxResponse(resp) + s.Require().NoError(err) + + t.Run("check that the solomachine is now active and that the clientstate is a pre-upgrade v2 solomachine clientstate", func(t *testing.T) { + status, err := s.QueryClientStatus(ctx, chainA, testvalues.SolomachineClientID(2)) + s.Require().NoError(err) + s.Require().Equal(exported.Active.String(), status) + + res, err := s.ClientState(ctx, chainA, testvalues.SolomachineClientID(2)) + s.Require().NoError(err) + s.Require().Equal(fmt.Sprint("/", proto.MessageName(&v7migrations.ClientState{})), res.ClientState.TypeUrl) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + s.Require().NoError(err) + s.AssertValidTxResponse(transferTxResp) + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := chainB.GetBalance(ctx, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + // create separate user specifically for the upgrade proposal to more easily verify starting + // and end balances of the chainA users. + chainAUpgradeProposalWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + t.Run("upgrade chainA", func(t *testing.T) { + s.UpgradeChain(ctx, chainA, chainAUpgradeProposalWallet, v7upgrades.UpgradeName, testCfg.ChainAConfig.Tag, testCfg.UpgradeTag) + }) + + t.Run("check that the tendermint clients are active again after upgrade", func(t *testing.T) { + status, err := s.QueryClientStatus(ctx, chainA, testvalues.TendermintClientID(0)) + s.Require().NoError(err) + s.Require().Equal(exported.Active.String(), status) + + status, err = s.QueryClientStatus(ctx, chainA, testvalues.TendermintClientID(1)) + s.Require().NoError(err) + s.Require().Equal(exported.Active.String(), status) + }) + + t.Run("IBC token transfer from chainA to chainB, to make sure the upgrade did not break the packet flow", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + s.Require().NoError(err) + s.AssertValidTxResponse(transferTxResp) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := chainB.GetBalance(ctx, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount * 2 + s.Require().Equal(expected, actualBalance) + }) + + t.Run("check that the v2 solo machine clientstate has been updated to the v3 solo machine clientstate", func(t *testing.T) { + res, err := s.ClientState(ctx, chainA, testvalues.SolomachineClientID(2)) + s.Require().NoError(err) + s.Require().Equal(fmt.Sprint("/", proto.MessageName(&solomachine.ClientState{})), res.ClientState.TypeUrl) + }) +} + // RegisterInterchainAccount will attempt to register an interchain account on the counterparty chain. func (s *UpgradeTestSuite) RegisterInterchainAccount(ctx context.Context, chain *cosmos.CosmosChain, user *ibc.Wallet, msgRegisterAccount *intertxtypes.MsgRegisterAccount) error { txResp, err := s.BroadcastMessages(ctx, chain, user, msgRegisterAccount) @@ -405,3 +568,16 @@ func getICAVersion(chainAVersion, chainBVersion string) string { // explicitly set the version string because the host chain might not yet support incentivized channels. return icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) } + +// ClientState queries the current ClientState by clientID +func (s *UpgradeTestSuite) ClientState(ctx context.Context, chain ibc.Chain, clientID string) (*clienttypes.QueryClientStateResponse, error) { + queryClient := s.GetChainGRCPClients(chain).ClientQueryClient + res, err := queryClient.ClientState(ctx, &clienttypes.QueryClientStateRequest{ + ClientId: clientID, + }) + if err != nil { + return res, err + } + + return res, nil +} diff --git a/e2e/testsuite/grpc_query.go b/e2e/testsuite/grpc_query.go index 214dd355340..a671f46d58a 100644 --- a/e2e/testsuite/grpc_query.go +++ b/e2e/testsuite/grpc_query.go @@ -34,6 +34,19 @@ func (s *E2ETestSuite) QueryClientState(ctx context.Context, chain ibc.Chain, cl return clientState, nil } +// QueryClientStatus queries the status of the client by clientID +func (s *E2ETestSuite) QueryClientStatus(ctx context.Context, chain ibc.Chain, clientID string) (string, error) { + queryClient := s.GetChainGRCPClients(chain).ClientQueryClient + res, err := queryClient.ClientStatus(ctx, &clienttypes.QueryClientStatusRequest{ + ClientId: clientID, + }) + if err != nil { + return "", err + } + + return res.Status, nil +} + // QueryChannel queries the channel on a given chain for the provided portID and channelID func (s *E2ETestSuite) QueryChannel(ctx context.Context, chain ibc.Chain, portID, channelID string) (channeltypes.Channel, error) { queryClient := s.GetChainGRCPClients(chain).ChannelQueryClient diff --git a/e2e/testsuite/testsuite.go b/e2e/testsuite/testsuite.go index 77c9482bf8a..b8c03f0e5f8 100644 --- a/e2e/testsuite/testsuite.go +++ b/e2e/testsuite/testsuite.go @@ -167,8 +167,8 @@ func (s *E2ETestSuite) SetupChainsRelayerAndChannel(ctx context.Context, channel time.Sleep(time.Second * 10) } - s.initGRPCClients(chainA) - s.initGRPCClients(chainB) + s.InitGRPCClients(chainA) + s.InitGRPCClients(chainB) chainAChannels, err := r.GetChannels(ctx, eRep, chainA.Config().ChainID) s.Require().NoError(err) @@ -361,9 +361,9 @@ func (s *E2ETestSuite) GetChainGRCPClients(chain ibc.Chain) GRPCClients { return cs } -// initGRPCClients establishes GRPC clients with the given chain. +// InitGRPCClients establishes GRPC clients with the given chain. // The created GRPCClients can be retrieved with GetChainGRCPClients. -func (s *E2ETestSuite) initGRPCClients(chain *cosmos.CosmosChain) { +func (s *E2ETestSuite) InitGRPCClients(chain *cosmos.CosmosChain) { // Create a connection to the gRPC server. grpcConn, err := grpc.Dial( chain.GetHostGRPCAddress(), diff --git a/e2e/testvalues/values.go b/e2e/testvalues/values.go index 70ee43aa924..3e95f5cddf8 100644 --- a/e2e/testvalues/values.go +++ b/e2e/testvalues/values.go @@ -1,6 +1,7 @@ package testvalues import ( + "fmt" "time" sdk "github.com/cosmos/cosmos-sdk/types" @@ -34,3 +35,11 @@ func DefaultFee(denom string) feetypes.Fee { func DefaultTransferAmount(denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdk.NewInt(IBCTransferAmount)} } + +func TendermintClientID(id int) string { + return fmt.Sprintf("07-tendermint-%d", id) +} + +func SolomachineClientID(id int) string { + return fmt.Sprint("06-solomachine-%d", id) +} From cd51cc9ec574a22f02474836ac7b3530a9a93c66 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Wed, 14 Dec 2022 11:05:37 +0100 Subject: [PATCH 34/44] e2e: update tags in e2e upgrade test --- .github/workflows/e2e-upgrade.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/e2e-upgrade.yaml b/.github/workflows/e2e-upgrade.yaml index 9432b52a4b9..e93d1ca434b 100644 --- a/.github/workflows/e2e-upgrade.yaml +++ b/.github/workflows/e2e-upgrade.yaml @@ -10,15 +10,15 @@ jobs: include: - test: TestV4ToV5ChainUpgrade chain-image: ghcr.io/cosmos/ibc-go-simd - chain-a-tag: v4.1.0 - chain-b-tag: v4.1.0 - chain-upgrade-tag: v5.0.1 + chain-a-tag: v4.2.0 + chain-b-tag: v4.2.0 + chain-upgrade-tag: v5.1.0 - test: TestV5ToV6ChainUpgrade chain-image: ghcr.io/cosmos/ibc-go-icad chain-binary: icad chain-a-tag: v0.3.5 chain-b-tag: v0.3.5 - chain-upgrade-tag: v0.4.0 + chain-upgrade-tag: v0.4.1 - test: TestV6ToV7ChainUpgrade chain-image: ghcr.io/cosmos/ibc-go-simd chain-binary: simd From 10788753dd9eb6b9fc7b01e4d743319db7df1e17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Wed, 14 Dec 2022 15:33:11 +0100 Subject: [PATCH 35/44] chore: use diffs to make registration more clear (#2927) --- docs/ibc/integration.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/ibc/integration.md b/docs/ibc/integration.md index 76fcabf4e5e..904fcdb303a 100644 --- a/docs/ibc/integration.md +++ b/docs/ibc/integration.md @@ -37,11 +37,11 @@ All light clients must be registered with `module.BasicManager` in a chain's app The following code example shows how to register the existing `ibctm.AppModuleBasic{}` light client implementation. -```go +```diff import ( ... - ibctm "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint" ++ ibctm "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint" ... ) @@ -55,7 +55,7 @@ var ( transfer.AppModuleBasic{}, // i.e ibc-transfer module // register light clients on IBC - ibctm.AppModuleBasic{}, ++ ibctm.AppModuleBasic{}, ) // module account permissions From 2ea8655129fef8e75ea5c70df2dc7382c98e3064 Mon Sep 17 00:00:00 2001 From: Auridas F Date: Wed, 14 Dec 2022 21:12:32 +0200 Subject: [PATCH 36/44] nit: fix typo in a comment --- modules/apps/transfer/keeper/msg_server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/apps/transfer/keeper/msg_server.go b/modules/apps/transfer/keeper/msg_server.go index 564647b2148..d6794c3bf3e 100644 --- a/modules/apps/transfer/keeper/msg_server.go +++ b/modules/apps/transfer/keeper/msg_server.go @@ -11,7 +11,7 @@ import ( var _ types.MsgServer = Keeper{} -// Transfer defines a rpc handler method for MsgTransfer. +// Transfer defines an rpc handler method for MsgTransfer. func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types.MsgTransferResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) From 3be0b6338233992e583721e763af19f3ac0b0544 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Thu, 15 Dec 2022 17:13:59 +0000 Subject: [PATCH 37/44] refactor: require light clients to set the initial client state and consensus state via the client state `Initialize` method (#2936) * set the initial client and consensus state via the client state Initialize method. update godocs * updating godocs * updating migration doc * updating light client guide docs for Initialize method * updating migration doc * Apply suggestions from code review Co-authored-by: Charly * Update docs/ibc/light-clients/client-state.md Co-authored-by: Carlos Rodriguez * adding tests to lightclients * updating 02-client tests Co-authored-by: Charly Co-authored-by: Carlos Rodriguez --- docs/ibc/light-clients/client-state.md | 5 ++-- docs/migrations/v6-to-v7.md | 4 +++- modules/core/02-client/keeper/client.go | 15 ++++-------- modules/core/02-client/keeper/client_test.go | 3 +++ modules/core/exported/client.go | 5 ++-- .../06-solomachine/client_state.go | 8 +++++-- .../06-solomachine/client_state_test.go | 9 ++++++-- .../07-tendermint/client_state.go | 14 +++++++---- .../07-tendermint/client_state_test.go | 23 ++++++++++++++----- 9 files changed, 55 insertions(+), 31 deletions(-) diff --git a/docs/ibc/light-clients/client-state.md b/docs/ibc/light-clients/client-state.md index fa32e90fe8a..024da207fbb 100644 --- a/docs/ibc/light-clients/client-state.md +++ b/docs/ibc/light-clients/client-state.md @@ -48,9 +48,10 @@ This value is used to facilitate timeouts by checking the packet timeout timesta ## `Initialize` method -Clients must validate the initial consensus state, and may store any client-specific metadata necessary. +Clients must validate the initial consensus state, and set the initial client state and consensus state in the provided client store. +Clients may also store any necessary client-specific metadata. -`Initialize` gets called when a [client is created](https://github.com/cosmos/ibc-go/blob/main/modules/core/02-client/keeper/client.go#L32). +`Initialize` is called when a [client is created](https://github.com/cosmos/ibc-go/blob/main/modules/core/02-client/keeper/client.go#L32). ## `VerifyMembership` method diff --git a/docs/migrations/v6-to-v7.md b/docs/migrations/v6-to-v7.md index 64cd0d54f28..6649b216f6e 100644 --- a/docs/migrations/v6-to-v7.md +++ b/docs/migrations/v6-to-v7.md @@ -106,7 +106,9 @@ The `VerifyUpgradeAndUpdateState` function has been modified. The client state a Light clients **must** handle all management of client and consensus states including the setting of updated client state and consensus state in the client store. -The `CheckHeaderAndUpdateState` function has been split into 4 new functions: +The `Initialize` method is now expected to set the initial client state, consensus state and any client-specific metadata in the provided store upon client creation. + +The `CheckHeaderAndUpdateState` method has been split into 4 new methods: - `VerifyClientMessage` verifies a `ClientMessage`. A `ClientMessage` could be a `Header`, `Misbehaviour`, or batch update. Calls to `CheckForMisbehaviour`, `UpdateState`, and `UpdateStateOnMisbehaviour` will assume that the content of the `ClientMessage` has been verified and can be trusted. An error should be returned if the `ClientMessage` fails to verify. diff --git a/modules/core/02-client/keeper/client.go b/modules/core/02-client/keeper/client.go index 1e7bbe9c26a..a593c682fd9 100644 --- a/modules/core/02-client/keeper/client.go +++ b/modules/core/02-client/keeper/client.go @@ -10,8 +10,9 @@ import ( "github.com/cosmos/ibc-go/v6/modules/core/exported" ) -// CreateClient creates a new client state and populates it with a given consensus -// state as defined in https://github.com/cosmos/ibc/tree/master/spec/core/ics-002-client-semantics#create +// CreateClient generates a new client identifier and isolated prefix store for the provided client state. +// The client state is responsible for setting any client-specific data in the store via the Initialize method. +// This includes the client state, initial consensus state and any associated metadata. func (k Keeper) CreateClient( ctx sdk.Context, clientState exported.ClientState, consensusState exported.ConsensusState, ) (string, error) { @@ -24,18 +25,12 @@ func (k Keeper) CreateClient( } clientID := k.GenerateClientIdentifier(ctx, clientState.ClientType()) + clientStore := k.ClientStore(ctx, clientID) - k.Logger(ctx).Info("client created at height", "client-id", clientID, "height", clientState.GetLatestHeight().String()) - - // verifies initial consensus state against client state and initializes client store with any client-specific metadata - // e.g. set ProcessedTime in Tendermint clients - if err := clientState.Initialize(ctx, k.cdc, k.ClientStore(ctx, clientID), consensusState); err != nil { + if err := clientState.Initialize(ctx, k.cdc, clientStore, consensusState); err != nil { return "", err } - k.SetClientState(ctx, clientID, clientState) - k.SetClientConsensusState(ctx, clientID, clientState.GetLatestHeight(), consensusState) - k.Logger(ctx).Info("client created at height", "client-id", clientID, "height", clientState.GetLatestHeight().String()) defer telemetry.IncrCounterWithLabels( diff --git a/modules/core/02-client/keeper/client_test.go b/modules/core/02-client/keeper/client_test.go index 12acddb4ff1..f20ba1dcdc1 100644 --- a/modules/core/02-client/keeper/client_test.go +++ b/modules/core/02-client/keeper/client_test.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v6/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v6/modules/core/24-host" "github.com/cosmos/ibc-go/v6/modules/core/exported" solomachine "github.com/cosmos/ibc-go/v6/modules/light-clients/06-solomachine" ibctm "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint" @@ -32,9 +33,11 @@ func (suite *KeeperTestSuite) TestCreateClient() { if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) suite.Require().NotNil(clientID, "valid test case %d failed: %s", i, tc.msg) + suite.Require().True(suite.keeper.ClientStore(suite.ctx, clientID).Has(host.ClientStateKey())) } else { suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) suite.Require().Equal("", clientID, "invalid test case %d passed: %s", i, tc.msg) + suite.Require().False(suite.keeper.ClientStore(suite.ctx, clientID).Has(host.ClientStateKey())) } } } diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index 81dea3373c5..3b2305beb71 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -59,9 +59,8 @@ type ClientState interface { height Height, ) (uint64, error) - // Initialization function - // Clients must validate the initial consensus state, and may store any client-specific metadata - // necessary for correct light client operation + // Initialize is called upon client creation, it allows the client to perform validation on the initial consensus state and set the + // client state, consensus state and any client-specific metadata necessary for correct light client operation in the provided client store. Initialize(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, consensusState ConsensusState) error // VerifyMembership is a generic proof verification method which verifies a proof of the existence of a value at a given CommitmentPath at the specified height. diff --git a/modules/light-clients/06-solomachine/client_state.go b/modules/light-clients/06-solomachine/client_state.go index ed2bf259418..5148a35aa62 100644 --- a/modules/light-clients/06-solomachine/client_state.go +++ b/modules/light-clients/06-solomachine/client_state.go @@ -77,12 +77,16 @@ func (cs ClientState) ZeroCustomFields() exported.ClientState { panic("ZeroCustomFields is not implemented as the solo machine implementation does not support upgrades.") } -// Initialize will check that initial consensus state is equal to the latest consensus state of the initial client. -func (cs ClientState) Initialize(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, consState exported.ConsensusState) error { +// Initialize checks that the initial consensus state is equal to the latest consensus state of the initial client and +// sets the client state in the provided client store. +func (cs ClientState) Initialize(_ sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, consState exported.ConsensusState) error { if !reflect.DeepEqual(cs.ConsensusState, consState) { return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "consensus state in initial client does not equal initial consensus state. expected: %s, got: %s", cs.ConsensusState, consState) } + + setClientState(clientStore, cdc, &cs) + return nil } diff --git a/modules/light-clients/06-solomachine/client_state_test.go b/modules/light-clients/06-solomachine/client_state_test.go index e494a484ec8..dbb958ae7ba 100644 --- a/modules/light-clients/06-solomachine/client_state_test.go +++ b/modules/light-clients/06-solomachine/client_state_test.go @@ -8,6 +8,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/v6/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v6/modules/core/24-host" "github.com/cosmos/ibc-go/v6/modules/core/exported" solomachine "github.com/cosmos/ibc-go/v6/modules/light-clients/06-solomachine" ibctm "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint" @@ -125,16 +126,20 @@ func (suite *SoloMachineTestSuite) TestInitialize() { } for _, tc := range testCases { + suite.SetupTest() + + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "solomachine") err := sm.ClientState().Initialize( suite.chainA.GetContext(), suite.chainA.Codec, - suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "solomachine"), - tc.consState, + store, tc.consState, ) if tc.expPass { suite.Require().NoError(err, "valid testcase: %s failed", tc.name) + suite.Require().True(store.Has(host.ClientStateKey())) } else { suite.Require().Error(err, "invalid testcase: %s passed", tc.name) + suite.Require().False(store.Has(host.ClientStateKey())) } } } diff --git a/modules/light-clients/07-tendermint/client_state.go b/modules/light-clients/07-tendermint/client_state.go index 9f6da36bf00..cc6fbe9c482 100644 --- a/modules/light-clients/07-tendermint/client_state.go +++ b/modules/light-clients/07-tendermint/client_state.go @@ -188,15 +188,19 @@ func (cs ClientState) ZeroCustomFields() exported.ClientState { } } -// Initialize will check that initial consensus state is a Tendermint consensus state -// and will store ProcessedTime for initial consensus state as ctx.BlockTime() -func (cs ClientState) Initialize(ctx sdk.Context, _ codec.BinaryCodec, clientStore sdk.KVStore, consState exported.ConsensusState) error { - if _, ok := consState.(*ConsensusState); !ok { +// Initialize checks that the initial consensus state is an 07-tendermint consensus state and +// sets the client state, consensus state and associated metadata in the provided client store. +func (cs ClientState) Initialize(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, consState exported.ConsensusState) error { + consensusState, ok := consState.(*ConsensusState) + if !ok { return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "invalid initial consensus state. expected type: %T, got: %T", &ConsensusState{}, consState) } - // set metadata for initial consensus state. + + setClientState(clientStore, cdc, &cs) + setConsensusState(clientStore, cdc, consensusState, cs.GetLatestHeight()) setConsensusMetadata(ctx, clientStore, cs.GetLatestHeight()) + return nil } diff --git a/modules/light-clients/07-tendermint/client_state_test.go b/modules/light-clients/07-tendermint/client_state_test.go index 64ffff0fd50..be7cccf2ce4 100644 --- a/modules/light-clients/07-tendermint/client_state_test.go +++ b/modules/light-clients/07-tendermint/client_state_test.go @@ -192,19 +192,30 @@ func (suite *TendermintTestSuite) TestInitialize() { }, } - path := ibctesting.NewPath(suite.chainA, suite.chainB) - err := path.EndpointA.CreateClient() - suite.Require().NoError(err) + for _, tc := range testCases { + suite.SetupTest() + path := ibctesting.NewPath(suite.chainA, suite.chainB) - clientState := suite.chainA.GetClientState(path.EndpointA.ClientID) - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + tmConfig, ok := path.EndpointB.ClientConfig.(*ibctesting.TendermintConfig) + suite.Require().True(ok) - for _, tc := range testCases { + clientState := ibctm.NewClientState( + path.EndpointB.Chain.ChainID, + tmConfig.TrustLevel, tmConfig.TrustingPeriod, tmConfig.UnbondingPeriod, tmConfig.MaxClockDrift, + suite.chainB.LastHeader.GetTrustedHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, + ) + + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) err := clientState.Initialize(suite.chainA.GetContext(), suite.chainA.Codec, store, tc.consensusState) + if tc.expPass { suite.Require().NoError(err, "valid case returned an error") + suite.Require().True(store.Has(host.ClientStateKey())) + suite.Require().True(store.Has(host.ConsensusStateKey(suite.chainB.LastHeader.GetTrustedHeight()))) } else { suite.Require().Error(err, "invalid case didn't return an error") + suite.Require().False(store.Has(host.ClientStateKey())) + suite.Require().False(store.Has(host.ConsensusStateKey(suite.chainB.LastHeader.GetTrustedHeight()))) } } } From e8f6eafaf7b123bed5bd35874b555c00d0481b9a Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Fri, 16 Dec 2022 13:04:37 +0100 Subject: [PATCH 38/44] chore: add backports for v4.3.x and v6.1.x --- .github/mergify.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/mergify.yml b/.github/mergify.yml index d0c92b1d2fb..0151ce6865d 100644 --- a/.github/mergify.yml +++ b/.github/mergify.yml @@ -65,7 +65,15 @@ pull_request_rules: actions: backport: branches: - - release/v4.2.x + - release/v4.2.x + - name: backport patches to v4.3.x branch + conditions: + - base=main + - label=backport-to-v4.3.x + actions: + backport: + branches: + - release/v4.3.x - name: backport patches to v5.2.x branch conditions: - base=main @@ -77,8 +85,8 @@ pull_request_rules: - name: backport patches to v6.0.x branch conditions: - base=main - - label=backport-to-v6.0.x + - label=backport-to-v6.1.x actions: backport: branches: - - release/v6.0.x + - release/v6.1.x From ee13f341880a53c6af91baf5e4548194829ab17b Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Fri, 16 Dec 2022 21:41:38 +0100 Subject: [PATCH 39/44] update compatibility tests with new release branches (#2946) * update compatibility tests * compatibility tests for v4.3.x * adding tags to tests * Skip e2e if test matrix does not exist (#2949) Co-authored-by: Carlos Rodriguez Co-authored-by: Cian Hatton --- .../incentivized-transfer-chain-a.json | 4 ++-- .../incentivized-transfer-chain-b.json | 4 ++-- .../transfer-chain-a.json | 4 ++-- .../transfer-chain-b.json | 4 ++-- .../release-v5.0.x/connection-chain-a.json | 10 ---------- .../release-v5.0.x/connection-chain-b.json | 10 ---------- .../release-v5.0.x/transfer-chain-a.json | 14 -------------- .../release-v5.0.x/transfer-chain-b.json | 14 -------------- .../incentivized-transfer-chain-a.json | 4 ++-- .../incentivized-transfer-chain-b.json | 4 ++-- .../transfer-chain-a.json | 4 ++-- .../transfer-chain-b.json | 4 ++-- .../release-v6.0.x/client-chain-a.json | 10 ---------- .../release-v6.0.x/client-chain-b.json | 10 ---------- .../client-chain-a.json | 4 ++-- .../client-chain-b.json | 4 ++-- .../connection-chain-a.json | 4 ++-- .../connection-chain-b.json | 4 ++-- .../ica-chain-a.json | 4 ++-- .../release-v6.1.x/ica-chain-b.json | 12 ++++++++++++ .../ica-gov-chain-a.json | 4 ++-- .../release-v6.1.x/ica-gov-chain-b.json | 10 ++++++++++ .../ica-groups-chain-a.json | 4 ++-- .../release-v6.1.x/ica-groups-chain-b.json | 10 ++++++++++ .../incentivized-ica-chain-a.json | 4 ++-- .../release-v6.1.x/incentivized-ica-chain-b.json | 11 +++++++++++ .../incentivized-transfer-chain-a.json | 4 ++-- .../incentivized-transfer-chain-b.json | 4 ++-- .../release-v6.1.x/transfer-chain-a.json | 15 +++++++++++++++ .../release-v6.1.x/transfer-chain-b.json | 15 +++++++++++++++ .../unreleased/client.json | 4 ++-- .../unreleased/connection.json | 4 ++-- .../unreleased/incentivized-transfer.json | 4 ++-- .../unreleased/transfer.json | 4 ++-- .../e2e-compatibility-workflow-call.yaml | 1 + .github/workflows/e2e-compatibility.yaml | 12 ++++++------ 36 files changed, 124 insertions(+), 118 deletions(-) rename .github/compatibility-test-matrices/{release-v5.0.x => release-v4.3.x}/incentivized-transfer-chain-a.json (80%) rename .github/compatibility-test-matrices/{release-v6.0.x => release-v4.3.x}/incentivized-transfer-chain-b.json (80%) rename .github/compatibility-test-matrices/{release-v6.0.x => release-v4.3.x}/transfer-chain-a.json (70%) rename .github/compatibility-test-matrices/{release-v6.0.x => release-v4.3.x}/transfer-chain-b.json (70%) delete mode 100644 .github/compatibility-test-matrices/release-v5.0.x/connection-chain-a.json delete mode 100644 .github/compatibility-test-matrices/release-v5.0.x/connection-chain-b.json delete mode 100644 .github/compatibility-test-matrices/release-v5.0.x/transfer-chain-a.json delete mode 100644 .github/compatibility-test-matrices/release-v5.0.x/transfer-chain-b.json rename .github/compatibility-test-matrices/{release-v6.0.x => release-v5.2.x}/incentivized-transfer-chain-a.json (80%) rename .github/compatibility-test-matrices/{release-v5.0.x => release-v5.2.x}/incentivized-transfer-chain-b.json (80%) rename .github/compatibility-test-matrices/{release-v5.1.x => release-v5.2.x}/transfer-chain-a.json (70%) rename .github/compatibility-test-matrices/{release-v5.1.x => release-v5.2.x}/transfer-chain-b.json (70%) delete mode 100644 .github/compatibility-test-matrices/release-v6.0.x/client-chain-a.json delete mode 100644 .github/compatibility-test-matrices/release-v6.0.x/client-chain-b.json rename .github/compatibility-test-matrices/{release-v5.0.x => release-v6.1.x}/client-chain-a.json (53%) rename .github/compatibility-test-matrices/{release-v5.0.x => release-v6.1.x}/client-chain-b.json (53%) rename .github/compatibility-test-matrices/{release-v6.0.x => release-v6.1.x}/connection-chain-a.json (53%) rename .github/compatibility-test-matrices/{release-v6.0.x => release-v6.1.x}/connection-chain-b.json (53%) rename .github/compatibility-test-matrices/{release-v6.0.x => release-v6.1.x}/ica-chain-a.json (79%) create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/ica-chain-b.json rename .github/compatibility-test-matrices/{release-v6.0.x => release-v6.1.x}/ica-gov-chain-a.json (71%) create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/ica-gov-chain-b.json rename .github/compatibility-test-matrices/{release-v6.0.x => release-v6.1.x}/ica-groups-chain-a.json (72%) create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/ica-groups-chain-b.json rename .github/compatibility-test-matrices/{release-v6.0.x => release-v6.1.x}/incentivized-ica-chain-a.json (77%) create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/incentivized-ica-chain-b.json rename .github/compatibility-test-matrices/{release-v5.1.x => release-v6.1.x}/incentivized-transfer-chain-a.json (80%) rename .github/compatibility-test-matrices/{release-v5.1.x => release-v6.1.x}/incentivized-transfer-chain-b.json (80%) create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/transfer-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v6.1.x/transfer-chain-b.json diff --git a/.github/compatibility-test-matrices/release-v5.0.x/incentivized-transfer-chain-a.json b/.github/compatibility-test-matrices/release-v4.3.x/incentivized-transfer-chain-a.json similarity index 80% rename from .github/compatibility-test-matrices/release-v5.0.x/incentivized-transfer-chain-a.json rename to .github/compatibility-test-matrices/release-v4.3.x/incentivized-transfer-chain-a.json index f24ef375ef1..9118ec66835 100644 --- a/.github/compatibility-test-matrices/release-v5.0.x/incentivized-transfer-chain-a.json +++ b/.github/compatibility-test-matrices/release-v4.3.x/incentivized-transfer-chain-a.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v5.0.x"], - "chain-b": ["release-v5.0.x", "v6.0.0", "v5.1.0", "v4.2.0", "v4.1.1"], + "chain-a": ["release-v4.3.x"], + "chain-b": ["release-v4.3.x", "v6.0.0", "v5.1.0", "v5.0.1", "4.2.0", "v4.1.1"], "entrypoint": ["TestIncentivizedTransferTestSuite"], "test": [ "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", diff --git a/.github/compatibility-test-matrices/release-v6.0.x/incentivized-transfer-chain-b.json b/.github/compatibility-test-matrices/release-v4.3.x/incentivized-transfer-chain-b.json similarity index 80% rename from .github/compatibility-test-matrices/release-v6.0.x/incentivized-transfer-chain-b.json rename to .github/compatibility-test-matrices/release-v4.3.x/incentivized-transfer-chain-b.json index 92c878dc74e..ccecdf24bd4 100644 --- a/.github/compatibility-test-matrices/release-v6.0.x/incentivized-transfer-chain-b.json +++ b/.github/compatibility-test-matrices/release-v4.3.x/incentivized-transfer-chain-b.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v6.0.x", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1"], - "chain-b": ["release-v6.0.x"], + "chain-a": ["release-v4.3.x", "v6.0.0", "v5.1.0", "v5.0.1", "4.2.0", "v4.1.1"], + "chain-b": ["release-v4.3.x"], "entrypoint": ["TestIncentivizedTransferTestSuite"], "test": [ "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", diff --git a/.github/compatibility-test-matrices/release-v6.0.x/transfer-chain-a.json b/.github/compatibility-test-matrices/release-v4.3.x/transfer-chain-a.json similarity index 70% rename from .github/compatibility-test-matrices/release-v6.0.x/transfer-chain-a.json rename to .github/compatibility-test-matrices/release-v4.3.x/transfer-chain-a.json index 1816d85a31f..8c1f50fc8f3 100644 --- a/.github/compatibility-test-matrices/release-v6.0.x/transfer-chain-a.json +++ b/.github/compatibility-test-matrices/release-v4.3.x/transfer-chain-a.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v6.0.x"], - "chain-b": ["release-v6.0.x", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-a": ["release-v4.3.x"], + "chain-b": ["release-v4.3.x", "v6.0.0", "v5.1.0", "v5.0.1", "4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "entrypoint": ["TestTransferTestSuite"], "test": [ "TestMsgTransfer_Succeeds_Nonincentivized", diff --git a/.github/compatibility-test-matrices/release-v6.0.x/transfer-chain-b.json b/.github/compatibility-test-matrices/release-v4.3.x/transfer-chain-b.json similarity index 70% rename from .github/compatibility-test-matrices/release-v6.0.x/transfer-chain-b.json rename to .github/compatibility-test-matrices/release-v4.3.x/transfer-chain-b.json index 5c9fb03ec13..0d8343afad0 100644 --- a/.github/compatibility-test-matrices/release-v6.0.x/transfer-chain-b.json +++ b/.github/compatibility-test-matrices/release-v4.3.x/transfer-chain-b.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v6.0.x", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], - "chain-b": ["release-v6.0.x"], + "chain-a": ["release-v4.3.x", "v6.0.0", "v5.1.0", "v5.0.1", "4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-b": ["release-v4.3.x"], "entrypoint": ["TestTransferTestSuite"], "test": [ "TestMsgTransfer_Succeeds_Nonincentivized", diff --git a/.github/compatibility-test-matrices/release-v5.0.x/connection-chain-a.json b/.github/compatibility-test-matrices/release-v5.0.x/connection-chain-a.json deleted file mode 100644 index 47e3170d3be..00000000000 --- a/.github/compatibility-test-matrices/release-v5.0.x/connection-chain-a.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "chain-a": ["release-v5.0.x"], - "chain-b": ["release-v5.0.x", "v6.0.0", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], - "entrypoint": ["TestConnectionTestSuite"], - "test": [ - "TestMaxExpectedTimePerBlockParam" - ], - "chain-binary": ["simd"], - "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] -} diff --git a/.github/compatibility-test-matrices/release-v5.0.x/connection-chain-b.json b/.github/compatibility-test-matrices/release-v5.0.x/connection-chain-b.json deleted file mode 100644 index 9547569730d..00000000000 --- a/.github/compatibility-test-matrices/release-v5.0.x/connection-chain-b.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "chain-a": ["release-v5.0.x", "v6.0.0", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], - "chain-b": ["release-v5.0.x"], - "entrypoint": ["TestConnectionTestSuite"], - "test": [ - "TestMaxExpectedTimePerBlockParam" - ], - "chain-binary": ["simd"], - "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] -} diff --git a/.github/compatibility-test-matrices/release-v5.0.x/transfer-chain-a.json b/.github/compatibility-test-matrices/release-v5.0.x/transfer-chain-a.json deleted file mode 100644 index 3454b8e7c5a..00000000000 --- a/.github/compatibility-test-matrices/release-v5.0.x/transfer-chain-a.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "chain-a": ["release-v5.0.x"], - "chain-b": ["release-v5.0.x", "v6.0.0", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], - "entrypoint": ["TestTransferTestSuite"], - "test": [ - "TestMsgTransfer_Succeeds_Nonincentivized", - "TestMsgTransfer_Fails_InvalidAddress", - "TestMsgTransfer_Timeout_Nonincentivized", - "TestSendEnabledParam", - "TestReceiveEnabledParam" - ], - "chain-binary": ["simd"], - "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] -} diff --git a/.github/compatibility-test-matrices/release-v5.0.x/transfer-chain-b.json b/.github/compatibility-test-matrices/release-v5.0.x/transfer-chain-b.json deleted file mode 100644 index 1e28e2a54ab..00000000000 --- a/.github/compatibility-test-matrices/release-v5.0.x/transfer-chain-b.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "chain-a": ["release-v5.0.x", "v6.0.0", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], - "chain-b": ["release-v5.0.x"], - "entrypoint": ["TestTransferTestSuite"], - "test": [ - "TestMsgTransfer_Succeeds_Nonincentivized", - "TestMsgTransfer_Fails_InvalidAddress", - "TestMsgTransfer_Timeout_Nonincentivized", - "TestSendEnabledParam", - "TestReceiveEnabledParam" - ], - "chain-binary": ["simd"], - "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] -} diff --git a/.github/compatibility-test-matrices/release-v6.0.x/incentivized-transfer-chain-a.json b/.github/compatibility-test-matrices/release-v5.2.x/incentivized-transfer-chain-a.json similarity index 80% rename from .github/compatibility-test-matrices/release-v6.0.x/incentivized-transfer-chain-a.json rename to .github/compatibility-test-matrices/release-v5.2.x/incentivized-transfer-chain-a.json index 58171d0556f..d4b3fb34a42 100644 --- a/.github/compatibility-test-matrices/release-v6.0.x/incentivized-transfer-chain-a.json +++ b/.github/compatibility-test-matrices/release-v5.2.x/incentivized-transfer-chain-a.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v6.0.x"], - "chain-b": ["release-v6.0.x", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1"], + "chain-a": ["release-v5.2.x"], + "chain-b": ["release-v5.2.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1"], "entrypoint": ["TestIncentivizedTransferTestSuite"], "test": [ "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", diff --git a/.github/compatibility-test-matrices/release-v5.0.x/incentivized-transfer-chain-b.json b/.github/compatibility-test-matrices/release-v5.2.x/incentivized-transfer-chain-b.json similarity index 80% rename from .github/compatibility-test-matrices/release-v5.0.x/incentivized-transfer-chain-b.json rename to .github/compatibility-test-matrices/release-v5.2.x/incentivized-transfer-chain-b.json index 66708272426..ae3619e6664 100644 --- a/.github/compatibility-test-matrices/release-v5.0.x/incentivized-transfer-chain-b.json +++ b/.github/compatibility-test-matrices/release-v5.2.x/incentivized-transfer-chain-b.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v5.0.x", "v6.0.0", "v5.1.0", "v4.2.0", "v4.1.1"], - "chain-b": ["release-v5.0.x"], + "chain-a": ["release-v5.2.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1"], + "chain-b": ["release-v5.2.x"], "entrypoint": ["TestIncentivizedTransferTestSuite"], "test": [ "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", diff --git a/.github/compatibility-test-matrices/release-v5.1.x/transfer-chain-a.json b/.github/compatibility-test-matrices/release-v5.2.x/transfer-chain-a.json similarity index 70% rename from .github/compatibility-test-matrices/release-v5.1.x/transfer-chain-a.json rename to .github/compatibility-test-matrices/release-v5.2.x/transfer-chain-a.json index ef4a7297cd3..9224d00e06e 100644 --- a/.github/compatibility-test-matrices/release-v5.1.x/transfer-chain-a.json +++ b/.github/compatibility-test-matrices/release-v5.2.x/transfer-chain-a.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v5.1.x"], - "chain-b": ["release-v5.1.x", "v6.0.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-a": ["release-v5.2.x"], + "chain-b": ["release-v5.2.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "entrypoint": ["TestTransferTestSuite"], "test": [ "TestMsgTransfer_Succeeds_Nonincentivized", diff --git a/.github/compatibility-test-matrices/release-v5.1.x/transfer-chain-b.json b/.github/compatibility-test-matrices/release-v5.2.x/transfer-chain-b.json similarity index 70% rename from .github/compatibility-test-matrices/release-v5.1.x/transfer-chain-b.json rename to .github/compatibility-test-matrices/release-v5.2.x/transfer-chain-b.json index 5da883292dd..e0694f7f645 100644 --- a/.github/compatibility-test-matrices/release-v5.1.x/transfer-chain-b.json +++ b/.github/compatibility-test-matrices/release-v5.2.x/transfer-chain-b.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v5.1.x", "v6.0.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], - "chain-b": ["release-v5.1.x"], + "chain-a": ["release-v5.2.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-b": ["release-v5.2.x"], "entrypoint": ["TestTransferTestSuite"], "test": [ "TestMsgTransfer_Succeeds_Nonincentivized", diff --git a/.github/compatibility-test-matrices/release-v6.0.x/client-chain-a.json b/.github/compatibility-test-matrices/release-v6.0.x/client-chain-a.json deleted file mode 100644 index 87d79b5ac0a..00000000000 --- a/.github/compatibility-test-matrices/release-v6.0.x/client-chain-a.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "chain-a": ["release-v6.0.x"], - "chain-b": ["release-v6.0.x", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], - "entrypoint": ["TestClientTestSuite"], - "test": [ - "TestClientUpdateProposal_Succeeds" - ], - "chain-binary": ["simd"], - "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] -} diff --git a/.github/compatibility-test-matrices/release-v6.0.x/client-chain-b.json b/.github/compatibility-test-matrices/release-v6.0.x/client-chain-b.json deleted file mode 100644 index 7d4ce6bb187..00000000000 --- a/.github/compatibility-test-matrices/release-v6.0.x/client-chain-b.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "chain-a": ["release-v6.0.x", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], - "chain-b": ["release-v6.0.x"], - "entrypoint": ["TestClientTestSuite"], - "test": [ - "TestClientUpdateProposal_Succeeds" - ], - "chain-binary": ["simd"], - "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] -} diff --git a/.github/compatibility-test-matrices/release-v5.0.x/client-chain-a.json b/.github/compatibility-test-matrices/release-v6.1.x/client-chain-a.json similarity index 53% rename from .github/compatibility-test-matrices/release-v5.0.x/client-chain-a.json rename to .github/compatibility-test-matrices/release-v6.1.x/client-chain-a.json index 52650b7a003..25fbd62822d 100644 --- a/.github/compatibility-test-matrices/release-v5.0.x/client-chain-a.json +++ b/.github/compatibility-test-matrices/release-v6.1.x/client-chain-a.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v5.0.x"], - "chain-b": ["release-v5.0.x", "v6.0.0", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-a": ["release-v6.1.x"], + "chain-b": ["release-v6.1.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "entrypoint": ["TestClientTestSuite"], "test": [ "TestClientUpdateProposal_Succeeds" diff --git a/.github/compatibility-test-matrices/release-v5.0.x/client-chain-b.json b/.github/compatibility-test-matrices/release-v6.1.x/client-chain-b.json similarity index 53% rename from .github/compatibility-test-matrices/release-v5.0.x/client-chain-b.json rename to .github/compatibility-test-matrices/release-v6.1.x/client-chain-b.json index 39f28546480..ab8f9659daa 100644 --- a/.github/compatibility-test-matrices/release-v5.0.x/client-chain-b.json +++ b/.github/compatibility-test-matrices/release-v6.1.x/client-chain-b.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v5.0.x", "v6.0.0", "v5.1.0", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], - "chain-b": ["release-v5.0.x"], + "chain-a": ["release-v6.1.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-b": ["release-v6.1.x"], "entrypoint": ["TestClientTestSuite"], "test": [ "TestClientUpdateProposal_Succeeds" diff --git a/.github/compatibility-test-matrices/release-v6.0.x/connection-chain-a.json b/.github/compatibility-test-matrices/release-v6.1.x/connection-chain-a.json similarity index 53% rename from .github/compatibility-test-matrices/release-v6.0.x/connection-chain-a.json rename to .github/compatibility-test-matrices/release-v6.1.x/connection-chain-a.json index 0e98f98fef1..858d6302770 100644 --- a/.github/compatibility-test-matrices/release-v6.0.x/connection-chain-a.json +++ b/.github/compatibility-test-matrices/release-v6.1.x/connection-chain-a.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v6.0.x"], - "chain-b": ["release-v6.0.x", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-a": ["release-v6.1.x"], + "chain-b": ["release-v6.1.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], "entrypoint": ["TestConnectionTestSuite"], "test": [ "TestMaxExpectedTimePerBlockParam" diff --git a/.github/compatibility-test-matrices/release-v6.0.x/connection-chain-b.json b/.github/compatibility-test-matrices/release-v6.1.x/connection-chain-b.json similarity index 53% rename from .github/compatibility-test-matrices/release-v6.0.x/connection-chain-b.json rename to .github/compatibility-test-matrices/release-v6.1.x/connection-chain-b.json index 8ce0206e5c2..c482cd3ee85 100644 --- a/.github/compatibility-test-matrices/release-v6.0.x/connection-chain-b.json +++ b/.github/compatibility-test-matrices/release-v6.1.x/connection-chain-b.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v6.0.x", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], - "chain-b": ["release-v6.0.x"], + "chain-a": ["release-v6.1.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-b": ["release-v6.1.x"], "entrypoint": ["TestConnectionTestSuite"], "test": [ "TestMaxExpectedTimePerBlockParam" diff --git a/.github/compatibility-test-matrices/release-v6.0.x/ica-chain-a.json b/.github/compatibility-test-matrices/release-v6.1.x/ica-chain-a.json similarity index 79% rename from .github/compatibility-test-matrices/release-v6.0.x/ica-chain-a.json rename to .github/compatibility-test-matrices/release-v6.1.x/ica-chain-a.json index 20d2e0c75f1..88015008626 100644 --- a/.github/compatibility-test-matrices/release-v6.0.x/ica-chain-a.json +++ b/.github/compatibility-test-matrices/release-v6.1.x/ica-chain-a.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v6.0.x"], - "chain-b": ["release-v6.0.x"], + "chain-a": ["release-v6.1.x"], + "chain-b": ["release-v6.1.x", "v6.0.0"], "entrypoint": ["TestInterchainAccountsTestSuite"], "test": [ "TestMsgSendTx_SuccessfulTransfer", diff --git a/.github/compatibility-test-matrices/release-v6.1.x/ica-chain-b.json b/.github/compatibility-test-matrices/release-v6.1.x/ica-chain-b.json new file mode 100644 index 00000000000..1f4b4ca2b63 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/ica-chain-b.json @@ -0,0 +1,12 @@ +{ + "chain-a": ["release-v6.1.x", "v6.0.0"], + "chain-b": ["release-v6.1.x"], + "entrypoint": ["TestInterchainAccountsTestSuite"], + "test": [ + "TestMsgSendTx_SuccessfulTransfer", + "TestMsgSendTx_FailedTransfer_InsufficientFunds", + "TestMsgSubmitTx_SuccessfulTransfer_AfterReopeningICA" + ], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} \ No newline at end of file diff --git a/.github/compatibility-test-matrices/release-v6.0.x/ica-gov-chain-a.json b/.github/compatibility-test-matrices/release-v6.1.x/ica-gov-chain-a.json similarity index 71% rename from .github/compatibility-test-matrices/release-v6.0.x/ica-gov-chain-a.json rename to .github/compatibility-test-matrices/release-v6.1.x/ica-gov-chain-a.json index 1f37ff750c4..066798a8f9a 100644 --- a/.github/compatibility-test-matrices/release-v6.0.x/ica-gov-chain-a.json +++ b/.github/compatibility-test-matrices/release-v6.1.x/ica-gov-chain-a.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v6.0.x"], - "chain-b": ["release-v6.0.x"], + "chain-a": ["release-v6.1.x", "v6.0.0"], + "chain-b": ["release-v6.1.x"], "entrypoint": ["TestInterchainAccountsGovTestSuite"], "test": [ "TestInterchainAccountsGovIntegration" diff --git a/.github/compatibility-test-matrices/release-v6.1.x/ica-gov-chain-b.json b/.github/compatibility-test-matrices/release-v6.1.x/ica-gov-chain-b.json new file mode 100644 index 00000000000..998681bc5ec --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/ica-gov-chain-b.json @@ -0,0 +1,10 @@ +{ + "chain-a": ["release-v6.1.x"], + "chain-b": ["release-v6.1.x", "v6.0.0"], + "entrypoint": ["TestInterchainAccountsGovTestSuite"], + "test": [ + "TestInterchainAccountsGovIntegration" + ], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} \ No newline at end of file diff --git a/.github/compatibility-test-matrices/release-v6.0.x/ica-groups-chain-a.json b/.github/compatibility-test-matrices/release-v6.1.x/ica-groups-chain-a.json similarity index 72% rename from .github/compatibility-test-matrices/release-v6.0.x/ica-groups-chain-a.json rename to .github/compatibility-test-matrices/release-v6.1.x/ica-groups-chain-a.json index 192011de384..1d8a4ec6e36 100644 --- a/.github/compatibility-test-matrices/release-v6.0.x/ica-groups-chain-a.json +++ b/.github/compatibility-test-matrices/release-v6.1.x/ica-groups-chain-a.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v6.0.x"], - "chain-b": ["release-v6.0.x"], + "chain-a": ["release-v6.1.x"], + "chain-b": ["release-v6.1.x", "v6.0.0"], "entrypoint": ["TestInterchainAccountsGroupsTestSuite"], "test": [ "TestInterchainAccountsGroupsIntegration" diff --git a/.github/compatibility-test-matrices/release-v6.1.x/ica-groups-chain-b.json b/.github/compatibility-test-matrices/release-v6.1.x/ica-groups-chain-b.json new file mode 100644 index 00000000000..3b44ff3a213 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/ica-groups-chain-b.json @@ -0,0 +1,10 @@ +{ + "chain-a": ["release-v6.1.x", "v6.0.0"], + "chain-b": ["release-v6.1.x"], + "entrypoint": ["TestInterchainAccountsGroupsTestSuite"], + "test": [ + "TestInterchainAccountsGroupsIntegration" + ], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} \ No newline at end of file diff --git a/.github/compatibility-test-matrices/release-v6.0.x/incentivized-ica-chain-a.json b/.github/compatibility-test-matrices/release-v6.1.x/incentivized-ica-chain-a.json similarity index 77% rename from .github/compatibility-test-matrices/release-v6.0.x/incentivized-ica-chain-a.json rename to .github/compatibility-test-matrices/release-v6.1.x/incentivized-ica-chain-a.json index 4f3eaa17eb9..aee80c12e16 100644 --- a/.github/compatibility-test-matrices/release-v6.0.x/incentivized-ica-chain-a.json +++ b/.github/compatibility-test-matrices/release-v6.1.x/incentivized-ica-chain-a.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v6.0.x"], - "chain-b": ["release-v6.0.x"], + "chain-a": ["release-v6.1.x"], + "chain-b": ["release-v6.1.x", "v6.0.0"], "entrypoint": ["TestIncentivizedInterchainAccountsTestSuite"], "test": [ "TestMsgSendTx_SuccessfulBankSend_Incentivized", diff --git a/.github/compatibility-test-matrices/release-v6.1.x/incentivized-ica-chain-b.json b/.github/compatibility-test-matrices/release-v6.1.x/incentivized-ica-chain-b.json new file mode 100644 index 00000000000..4596dd2a417 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/incentivized-ica-chain-b.json @@ -0,0 +1,11 @@ +{ + "chain-a": ["release-v6.1.x", "v6.0.0"], + "chain-b": ["release-v6.1.x"], + "entrypoint": ["TestIncentivizedInterchainAccountsTestSuite"], + "test": [ + "TestMsgSendTx_SuccessfulBankSend_Incentivized", + "TestMsgSendTx_FailedBankSend_Incentivized" + ], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} \ No newline at end of file diff --git a/.github/compatibility-test-matrices/release-v5.1.x/incentivized-transfer-chain-a.json b/.github/compatibility-test-matrices/release-v6.1.x/incentivized-transfer-chain-a.json similarity index 80% rename from .github/compatibility-test-matrices/release-v5.1.x/incentivized-transfer-chain-a.json rename to .github/compatibility-test-matrices/release-v6.1.x/incentivized-transfer-chain-a.json index a17abd2c4d5..8b30472ad6c 100644 --- a/.github/compatibility-test-matrices/release-v5.1.x/incentivized-transfer-chain-a.json +++ b/.github/compatibility-test-matrices/release-v6.1.x/incentivized-transfer-chain-a.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v5.1.x"], - "chain-b": ["release-v5.1.x", "v6.0.0", "v5.0.1", "v4.2.0", "v4.1.1"], + "chain-a": ["release-v6.1.x"], + "chain-b": ["release-v6.1.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1"], "entrypoint": ["TestIncentivizedTransferTestSuite"], "test": [ "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", diff --git a/.github/compatibility-test-matrices/release-v5.1.x/incentivized-transfer-chain-b.json b/.github/compatibility-test-matrices/release-v6.1.x/incentivized-transfer-chain-b.json similarity index 80% rename from .github/compatibility-test-matrices/release-v5.1.x/incentivized-transfer-chain-b.json rename to .github/compatibility-test-matrices/release-v6.1.x/incentivized-transfer-chain-b.json index e1289a6f1f4..f884a747359 100644 --- a/.github/compatibility-test-matrices/release-v5.1.x/incentivized-transfer-chain-b.json +++ b/.github/compatibility-test-matrices/release-v6.1.x/incentivized-transfer-chain-b.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v5.1.x", "v6.0.0", "v5.0.1", "v4.2.0", "v4.1.1"], - "chain-b": ["release-v5.1.x"], + "chain-a": ["release-v6.1.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1"], + "chain-b": ["release-v6.1.x"], "entrypoint": ["TestIncentivizedTransferTestSuite"], "test": [ "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", diff --git a/.github/compatibility-test-matrices/release-v6.1.x/transfer-chain-a.json b/.github/compatibility-test-matrices/release-v6.1.x/transfer-chain-a.json new file mode 100644 index 00000000000..e2e2ca34ace --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/transfer-chain-a.json @@ -0,0 +1,15 @@ +{ + "chain-a": ["release-v6.1.x"], + "chain-b": ["release-v6.1.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "entrypoint": ["TestTransferTestSuite"], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized", + "TestMsgTransfer_Fails_InvalidAddress", + "TestMsgTransfer_Timeout_Nonincentivized", + "TestMsgTransfer_WithMemo", + "TestSendEnabledParam", + "TestReceiveEnabledParam" + ], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/release-v6.1.x/transfer-chain-b.json b/.github/compatibility-test-matrices/release-v6.1.x/transfer-chain-b.json new file mode 100644 index 00000000000..666123c6668 --- /dev/null +++ b/.github/compatibility-test-matrices/release-v6.1.x/transfer-chain-b.json @@ -0,0 +1,15 @@ +{ + "chain-a": ["release-v6.1.x", "v6.0.0", "v5.1.0", "v5.0.1", "v4.2.0", "v4.1.1", "v3.4.0", "v3.3.1", "v2.5.0", "v2.4.2"], + "chain-b": ["release-v6.1.x"], + "entrypoint": ["TestTransferTestSuite"], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized", + "TestMsgTransfer_Fails_InvalidAddress", + "TestMsgTransfer_Timeout_Nonincentivized", + "TestMsgTransfer_WithMemo", + "TestSendEnabledParam", + "TestReceiveEnabledParam" + ], + "chain-binary": ["simd"], + "chain-image": ["ghcr.io/cosmos/ibc-go-simd"] +} diff --git a/.github/compatibility-test-matrices/unreleased/client.json b/.github/compatibility-test-matrices/unreleased/client.json index d6859f4b370..b8db51082ef 100644 --- a/.github/compatibility-test-matrices/unreleased/client.json +++ b/.github/compatibility-test-matrices/unreleased/client.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v6.0.x", "release-v5.1.x", "release-v5.0.x", "release-v4.2.x", "release-v3.4.x", "release-v2.5.x"], - "chain-b": ["release-v6.0.x", "release-v5.1.x", "release-v5.0.x", "release-v4.2.x", "release-v3.4.x", "release-v2.5.x"], + "chain-a": ["release-v6.1.x", "release-v5.2.x", "release-v4.3.x", "release-v4.2.x", "release-v3.4.x", "release-v2.5.x"], + "chain-b": ["release-v6.1.x", "release-v5.2.x", "release-v4.3.x", "release-v4.2.x", "release-v3.4.x", "release-v2.5.x"], "entrypoint": ["TestClientTestSuite"], "test": [ "TestClientUpdateProposal_Succeeds" diff --git a/.github/compatibility-test-matrices/unreleased/connection.json b/.github/compatibility-test-matrices/unreleased/connection.json index 9594c65fb0d..4d4808790c4 100644 --- a/.github/compatibility-test-matrices/unreleased/connection.json +++ b/.github/compatibility-test-matrices/unreleased/connection.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v6.0.x", "release-v5.1.x", "release-v5.0.x", "release-v4.2.x", "release-v3.4.x", "release-v2.5.x"], - "chain-b": ["release-v6.0.x", "release-v5.1.x", "release-v5.0.x", "release-v4.2.x", "release-v3.4.x", "release-v2.5.x"], + "chain-a": ["release-v6.1.x", "release-v5.2.x", "release-v4.3.x", "release-v4.2.x", "release-v3.4.x", "release-v2.5.x"], + "chain-b": ["release-v6.1.x", "release-v5.2.x", "release-v4.3.x", "release-v4.2.x", "release-v3.4.x", "release-v2.5.x"], "entrypoint": ["TestConnectionTestSuite"], "test": [ "TestMaxExpectedTimePerBlockParam" diff --git a/.github/compatibility-test-matrices/unreleased/incentivized-transfer.json b/.github/compatibility-test-matrices/unreleased/incentivized-transfer.json index b20429a2b64..22dc778de45 100644 --- a/.github/compatibility-test-matrices/unreleased/incentivized-transfer.json +++ b/.github/compatibility-test-matrices/unreleased/incentivized-transfer.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v6.0.x", "release-v5.1.x", "release-v5.0.x", "release-v4.2.x"], - "chain-b": ["release-v6.0.x", "release-v5.1.x", "release-v5.0.x", "release-v4.2.x"], + "chain-a": ["release-v6.1.x", "release-v5.2.x", "release-v4.3.x", "release-v4.2.x"], + "chain-b": ["release-v6.1.x", "release-v5.2.x", "release-v4.3.x", "release-v4.2.x"], "entrypoint": ["TestIncentivizedTransferTestSuite"], "test": [ "TestMsgPayPacketFee_AsyncSingleSender_Succeeds", diff --git a/.github/compatibility-test-matrices/unreleased/transfer.json b/.github/compatibility-test-matrices/unreleased/transfer.json index 1183d941ba9..4656add0d97 100644 --- a/.github/compatibility-test-matrices/unreleased/transfer.json +++ b/.github/compatibility-test-matrices/unreleased/transfer.json @@ -1,6 +1,6 @@ { - "chain-a": ["release-v6.0.x", "release-v5.1.x", "release-v4.2.x", "release-v3.4.x", "release-v2.5.x"], - "chain-b": ["release-v6.0.x", "release-v5.1.x", "release-v4.2.x", "release-v3.4.x", "release-v2.5.x"], + "chain-a": ["release-v6.1.x", "release-v5.2.x", "release-v4.2.x", "release-v3.4.x", "release-v2.5.x"], + "chain-b": ["release-v6.1.x", "release-v5.2.x", "release-v4.2.x", "release-v3.4.x", "release-v2.5.x"], "entrypoint": ["TestTransferTestSuite"], "test": [ "TestMsgTransfer_Succeeds_Nonincentivized", diff --git a/.github/workflows/e2e-compatibility-workflow-call.yaml b/.github/workflows/e2e-compatibility-workflow-call.yaml index 3e438ac0026..e8315883462 100644 --- a/.github/workflows/e2e-compatibility-workflow-call.yaml +++ b/.github/workflows/e2e-compatibility-workflow-call.yaml @@ -32,6 +32,7 @@ jobs: e2e: runs-on: ubuntu-latest needs: load-test-matrix + if: needs.load-test-matrix.outputs.test-matrix strategy: fail-fast: false matrix: ${{ fromJSON(needs.load-test-matrix.outputs.test-matrix) }} diff --git a/.github/workflows/e2e-compatibility.yaml b/.github/workflows/e2e-compatibility.yaml index 53158a0bd1b..2d2c1dec237 100644 --- a/.github/workflows/e2e-compatibility.yaml +++ b/.github/workflows/e2e-compatibility.yaml @@ -10,9 +10,9 @@ on: - release/v2.5.x - release/v3.4.x - release/v4.2.x - - release/v5.0.x - - release/v5.1.x - - release/v6.0.x + - release/v4.3.x + - release/v5.2.x + - release/v6.1.x - main env: @@ -43,9 +43,9 @@ jobs: - release/v2.5.x - release/v3.4.x - release/v4.2.x - - release/v5.0.x - - release/v5.1.x - - release/v6.0.x + - release/v4.3.x + - release/v5.2.x + - release/v6.1.x - main steps: - uses: actions/checkout@v3 From d3be95a6cca575897e9383257c355c64ea1edf14 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Sat, 17 Dec 2022 21:54:30 +0100 Subject: [PATCH 40/44] fix(statemachine)!: check x/bank send enabled before escrowing fees --- CHANGELOG.md | 1 + modules/apps/29-fee/keeper/msg_server.go | 8 ++++ modules/apps/29-fee/keeper/msg_server_test.go | 45 +++++++++++++++++++ modules/apps/29-fee/types/expected_keepers.go | 1 + 4 files changed, 55 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a9da1f226a..0333484265a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (06-solomachine) [\#2744](https://github.com/cosmos/ibc-go/pull/2744) `Misbehaviour.ValidateBasic()` now only enforces that signature data does not match when the signature paths are different. * (06-solomachine) [\#2748](https://github.com/cosmos/ibc-go/pull/2748) Adding sentinel value for header path in 06-solomachine. +* (apps/29-fee) [\#2942](https://github.com/cosmos/ibc-go/pull/2942) Check `x/bank` send enabled before escrowing fees. ### Improvements diff --git a/modules/apps/29-fee/keeper/msg_server.go b/modules/apps/29-fee/keeper/msg_server.go index eba43c563b8..5e2587b48e4 100644 --- a/modules/apps/29-fee/keeper/msg_server.go +++ b/modules/apps/29-fee/keeper/msg_server.go @@ -92,6 +92,10 @@ func (k Keeper) PayPacketFee(goCtx context.Context, msg *types.MsgPayPacketFee) return nil, err } + if err := k.bankKeeper.IsSendEnabledCoins(ctx, msg.Fee.Total()...); err != nil { + return nil, err + } + if k.bankKeeper.BlockedAddr(refundAcc) { return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to escrow fees", refundAcc) } @@ -133,6 +137,10 @@ func (k Keeper) PayPacketFeeAsync(goCtx context.Context, msg *types.MsgPayPacket return nil, err } + if err := k.bankKeeper.IsSendEnabledCoins(ctx, msg.PacketFee.Fee.Total()...); err != nil { + return nil, err + } + if k.bankKeeper.BlockedAddr(refundAcc) { return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to escrow fees", refundAcc) } diff --git a/modules/apps/29-fee/keeper/msg_server_test.go b/modules/apps/29-fee/keeper/msg_server_test.go index 7f8f08744d8..d767b3f4361 100644 --- a/modules/apps/29-fee/keeper/msg_server_test.go +++ b/modules/apps/29-fee/keeper/msg_server_test.go @@ -2,6 +2,7 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/ibc-go/v6/modules/apps/29-fee/types" transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" @@ -197,6 +198,17 @@ func (suite *KeeperTestSuite) TestPayPacketFee() { }, true, }, + { + "bank send enabled for fee denom", + func() { + suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), + banktypes.Params{ + SendEnabled: []*banktypes.SendEnabled{{Denom: sdk.DefaultBondDenom, Enabled: true}}, + }, + ) + }, + true, + }, { "refund account is module account", func() { @@ -244,6 +256,17 @@ func (suite *KeeperTestSuite) TestPayPacketFee() { }, false, }, + { + "bank send disabled for fee denom", + func() { + suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), + banktypes.Params{ + SendEnabled: []*banktypes.SendEnabled{{Denom: sdk.DefaultBondDenom, Enabled: false}}, + }, + ) + }, + false, + }, { "acknowledgement fee balance not found", func() { @@ -347,6 +370,17 @@ func (suite *KeeperTestSuite) TestPayPacketFeeAsync() { }, true, }, + { + "bank send enabled for fee denom", + func() { + suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), + banktypes.Params{ + SendEnabled: []*banktypes.SendEnabled{{Denom: sdk.DefaultBondDenom, Enabled: true}}, + }, + ) + }, + true, + }, { "fee module is locked", func() { @@ -432,6 +466,17 @@ func (suite *KeeperTestSuite) TestPayPacketFeeAsync() { }, false, }, + { + "bank send disabled for fee denom", + func() { + suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), + banktypes.Params{ + SendEnabled: []*banktypes.SendEnabled{{Denom: sdk.DefaultBondDenom, Enabled: false}}, + }, + ) + }, + false, + }, { "acknowledgement fee balance not found", func() { diff --git a/modules/apps/29-fee/types/expected_keepers.go b/modules/apps/29-fee/types/expected_keepers.go index c7168eef370..06f67f4cdcd 100644 --- a/modules/apps/29-fee/types/expected_keepers.go +++ b/modules/apps/29-fee/types/expected_keepers.go @@ -33,4 +33,5 @@ type BankKeeper interface { SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error BlockedAddr(sdk.AccAddress) bool + IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error } From 6604b593970bf8e28f8ce40f721d5454115a5299 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 10:54:10 +0100 Subject: [PATCH 41/44] chore(deps): bump goreleaser/goreleaser-action from 3 to 4 (#2932) Bumps [goreleaser/goreleaser-action](https://github.com/goreleaser/goreleaser-action) from 3 to 4. - [Release notes](https://github.com/goreleaser/goreleaser-action/releases) - [Commits](https://github.com/goreleaser/goreleaser-action/compare/v3...v4) --- updated-dependencies: - dependency-name: goreleaser/goreleaser-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Carlos Rodriguez --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6fe0111d8e0..def7fd287c7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,7 @@ jobs: go-version: '1.18' - name: Release - uses: goreleaser/goreleaser-action@v3 + uses: goreleaser/goreleaser-action@v4 if: startsWith(github.ref, 'refs/tags/') with: version: latest From ddc4f2d3b9d75751271f94f6b580c06980008e4a Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Mon, 19 Dec 2022 12:11:20 +0000 Subject: [PATCH 42/44] chore(api)!: removing solomachine header sequence (#2941) * removing solomachine header sequence * removing commented out code in validate basic --- .../light-clients/06-solomachine/header.go | 6 +- .../06-solomachine/header_test.go | 15 -- .../06-solomachine/solomachine.pb.go | 141 +++++++----------- .../light-clients/06-solomachine/update.go | 10 +- .../06-solomachine/update_test.go | 11 -- .../solomachine/v3/solomachine.proto | 25 ++-- testing/solomachine.go | 1 - 7 files changed, 71 insertions(+), 138 deletions(-) diff --git a/modules/light-clients/06-solomachine/header.go b/modules/light-clients/06-solomachine/header.go index 633c25425bc..382eddfe34e 100644 --- a/modules/light-clients/06-solomachine/header.go +++ b/modules/light-clients/06-solomachine/header.go @@ -36,13 +36,9 @@ func (h Header) GetPubKey() (cryptotypes.PubKey, error) { return publicKey, nil } -// ValidateBasic ensures that the sequence, signature and public key have all +// ValidateBasic ensures that the timestamp, signature and public key have all // been initialized. func (h Header) ValidateBasic() error { - if h.Sequence == 0 { - return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "sequence number cannot be zero") - } - if h.Timestamp == 0 { return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "timestamp cannot be zero") } diff --git a/modules/light-clients/06-solomachine/header_test.go b/modules/light-clients/06-solomachine/header_test.go index 7d752d88a8c..4aa9e506711 100644 --- a/modules/light-clients/06-solomachine/header_test.go +++ b/modules/light-clients/06-solomachine/header_test.go @@ -22,21 +22,9 @@ func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { header, true, }, - { - "sequence is zero", - &solomachine.Header{ - Sequence: 0, - Timestamp: header.Timestamp, - Signature: header.Signature, - NewPublicKey: header.NewPublicKey, - NewDiversifier: header.NewDiversifier, - }, - false, - }, { "timestamp is zero", &solomachine.Header{ - Sequence: header.Sequence, Timestamp: 0, Signature: header.Signature, NewPublicKey: header.NewPublicKey, @@ -47,7 +35,6 @@ func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { { "signature is empty", &solomachine.Header{ - Sequence: header.Sequence, Timestamp: header.Timestamp, Signature: []byte{}, NewPublicKey: header.NewPublicKey, @@ -58,7 +45,6 @@ func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { { "diversifier contains only spaces", &solomachine.Header{ - Sequence: header.Sequence, Timestamp: header.Timestamp, Signature: header.Signature, NewPublicKey: header.NewPublicKey, @@ -69,7 +55,6 @@ func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { { "public key is nil", &solomachine.Header{ - Sequence: header.Sequence, Timestamp: header.Timestamp, Signature: header.Signature, NewPublicKey: nil, diff --git a/modules/light-clients/06-solomachine/solomachine.pb.go b/modules/light-clients/06-solomachine/solomachine.pb.go index 7d2f64ed5ce..a53e2f0380f 100644 --- a/modules/light-clients/06-solomachine/solomachine.pb.go +++ b/modules/light-clients/06-solomachine/solomachine.pb.go @@ -115,12 +115,10 @@ var xxx_messageInfo_ConsensusState proto.InternalMessageInfo // Header defines a solo machine consensus header type Header struct { - // sequence to update solo machine public key at - Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` - Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` - NewPublicKey *types.Any `protobuf:"bytes,4,opt,name=new_public_key,json=newPublicKey,proto3" json:"new_public_key,omitempty" yaml:"new_public_key"` - NewDiversifier string `protobuf:"bytes,5,opt,name=new_diversifier,json=newDiversifier,proto3" json:"new_diversifier,omitempty" yaml:"new_diversifier"` + Timestamp uint64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` + NewPublicKey *types.Any `protobuf:"bytes,3,opt,name=new_public_key,json=newPublicKey,proto3" json:"new_public_key,omitempty" yaml:"new_public_key"` + NewDiversifier string `protobuf:"bytes,4,opt,name=new_diversifier,json=newDiversifier,proto3" json:"new_diversifier,omitempty" yaml:"new_diversifier"` } func (m *Header) Reset() { *m = Header{} } @@ -383,52 +381,52 @@ func init() { } var fileDescriptor_264187157b9220a4 = []byte{ - // 711 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xcd, 0x4e, 0xdb, 0x4a, - 0x14, 0xce, 0x04, 0x83, 0xc8, 0x24, 0x37, 0x70, 0xad, 0x80, 0x42, 0x74, 0x15, 0x47, 0x5e, 0xb1, - 0xc1, 0xbe, 0x80, 0xc4, 0x82, 0xbb, 0xb9, 0x04, 0x54, 0x55, 0x2d, 0x55, 0x2b, 0x43, 0x37, 0xdd, - 0x44, 0x63, 0x67, 0xe2, 0x8c, 0x6a, 0xcf, 0xa4, 0x99, 0x71, 0xa2, 0x54, 0x5d, 0x54, 0x5d, 0x75, - 0xd9, 0x45, 0xbb, 0xaf, 0x2a, 0xf5, 0x5d, 0xba, 0x64, 0xc9, 0x2a, 0xaa, 0xe0, 0x0d, 0xf2, 0x04, - 0x95, 0xc7, 0x4e, 0xfc, 0x03, 0x05, 0xa9, 0xed, 0x6e, 0xce, 0x99, 0xf3, 0xf3, 0x7d, 0xdf, 0x9c, - 0x63, 0xc3, 0x5d, 0x62, 0x3b, 0xa6, 0x47, 0xdc, 0xbe, 0x70, 0x3c, 0x82, 0xa9, 0xe0, 0x26, 0x67, - 0x1e, 0xf3, 0x91, 0xd3, 0x27, 0x14, 0x9b, 0xa3, 0xfd, 0xb4, 0x69, 0x0c, 0x86, 0x4c, 0x30, 0x55, - 0x23, 0xb6, 0x63, 0xa4, 0x53, 0x8c, 0x74, 0xcc, 0x68, 0xbf, 0x51, 0x73, 0x99, 0xcb, 0x64, 0xac, - 0x19, 0x9e, 0xa2, 0xb4, 0xc6, 0x96, 0xcb, 0x98, 0xeb, 0x61, 0x53, 0x5a, 0x76, 0xd0, 0x33, 0x11, - 0x9d, 0x44, 0x57, 0xfa, 0x25, 0x80, 0xe5, 0x63, 0x59, 0xeb, 0x4c, 0x20, 0x81, 0xd5, 0x06, 0x5c, - 0xe5, 0xf8, 0x55, 0x80, 0xa9, 0x83, 0xeb, 0xa0, 0x05, 0xb6, 0x15, 0x6b, 0x61, 0xab, 0xbb, 0xb0, - 0x44, 0x78, 0xa7, 0x37, 0x64, 0xaf, 0x31, 0xad, 0x17, 0x5b, 0x60, 0x7b, 0xb5, 0x5d, 0x9b, 0x4d, - 0xb5, 0xf5, 0x09, 0xf2, 0xbd, 0x43, 0x7d, 0x71, 0xa5, 0x5b, 0xab, 0x84, 0x3f, 0x90, 0x47, 0x55, - 0xc0, 0x35, 0x87, 0x51, 0x8e, 0x29, 0x0f, 0x78, 0x87, 0x87, 0x1d, 0xea, 0x4b, 0x2d, 0xb0, 0x5d, - 0xde, 0x33, 0x8d, 0x7b, 0xa8, 0x18, 0xc7, 0xf3, 0x3c, 0x09, 0xac, 0xdd, 0x98, 0x4d, 0xb5, 0xcd, - 0xa8, 0x53, 0xae, 0xa2, 0x6e, 0x55, 0x9d, 0x4c, 0xec, 0xa1, 0xf2, 0xfe, 0xb3, 0x56, 0xd0, 0xbf, - 0x00, 0x58, 0xcd, 0x16, 0x51, 0x1f, 0x41, 0x38, 0x08, 0x6c, 0x8f, 0x38, 0x9d, 0x97, 0x78, 0x22, - 0xf9, 0x95, 0xf7, 0x6a, 0x46, 0xa4, 0x8e, 0x31, 0x57, 0xc7, 0x38, 0xa2, 0x93, 0xf6, 0xc6, 0x6c, - 0xaa, 0xfd, 0x1d, 0xb5, 0x4b, 0x32, 0x74, 0xab, 0x14, 0x19, 0x8f, 0xf1, 0x44, 0x6d, 0xc1, 0x72, - 0x97, 0x8c, 0xf0, 0x90, 0x93, 0x1e, 0xc1, 0x43, 0xa9, 0x47, 0xc9, 0x4a, 0xbb, 0xd4, 0x7f, 0x60, - 0x49, 0x10, 0x1f, 0x73, 0x81, 0xfc, 0x81, 0xa4, 0xad, 0x58, 0x89, 0x23, 0x06, 0xf9, 0xae, 0x08, - 0x57, 0x1e, 0x62, 0xd4, 0xc5, 0xc3, 0x3b, 0xa5, 0xcf, 0x94, 0x2a, 0xe6, 0x4a, 0x85, 0xb7, 0x9c, - 0xb8, 0x14, 0x89, 0x60, 0x18, 0xe9, 0x5b, 0xb1, 0x12, 0x87, 0xfa, 0x1c, 0x56, 0x29, 0x1e, 0x77, - 0x52, 0xc4, 0x95, 0x3b, 0x88, 0x6f, 0xcd, 0xa6, 0xda, 0x46, 0x44, 0x3c, 0x9b, 0xa5, 0x5b, 0x15, - 0x8a, 0xc7, 0xcf, 0x16, 0xfc, 0x8f, 0xe1, 0x5a, 0x18, 0x90, 0xd6, 0x60, 0x39, 0xd4, 0x20, 0xfd, - 0x52, 0xb9, 0x00, 0xdd, 0x0a, 0x91, 0x9c, 0x24, 0x8e, 0x58, 0x84, 0x8f, 0x45, 0x58, 0x79, 0x42, - 0xb8, 0x8d, 0xfb, 0x68, 0x44, 0x58, 0x70, 0xb7, 0x14, 0x03, 0xf8, 0xd7, 0x82, 0x5b, 0x87, 0x51, - 0x2c, 0xe5, 0x28, 0xef, 0xed, 0xde, 0x3b, 0x50, 0x67, 0xf3, 0xac, 0x23, 0xda, 0x3d, 0x41, 0x02, - 0xb5, 0xeb, 0xb3, 0xa9, 0x56, 0x8b, 0x80, 0x66, 0x2a, 0xea, 0x56, 0x65, 0x61, 0x3f, 0xa5, 0xb9, - 0x8e, 0x62, 0xcc, 0xe2, 0x11, 0xfe, 0x53, 0x1d, 0xc5, 0x98, 0xa5, 0x3b, 0x9e, 0x8f, 0x59, 0x2c, - 0xcb, 0x1b, 0xb8, 0x9e, 0xaf, 0x90, 0x7d, 0x6a, 0x90, 0x7f, 0x6a, 0x15, 0x2a, 0x03, 0x24, 0xfa, - 0x52, 0x92, 0x8a, 0x25, 0xcf, 0xa1, 0xaf, 0x8b, 0x04, 0x8a, 0xe7, 0x42, 0x9e, 0xb3, 0xe3, 0xa4, - 0xdc, 0x3e, 0x99, 0x6f, 0x01, 0xac, 0x9f, 0xcf, 0x7d, 0xb8, 0xbb, 0x40, 0x22, 0x61, 0xfc, 0x0f, - 0xab, 0x09, 0x01, 0x59, 0x5e, 0x62, 0x49, 0x4f, 0x4f, 0xf6, 0x5e, 0xb7, 0x12, 0x0d, 0x4f, 0x6e, - 0x40, 0x28, 0xde, 0x0e, 0xe1, 0x13, 0x80, 0xa5, 0xb0, 0x6f, 0x7b, 0x22, 0x30, 0xff, 0x8d, 0xfd, - 0xc8, 0xad, 0xea, 0xd2, 0xcd, 0x55, 0x9d, 0x0b, 0xa7, 0xdc, 0x22, 0xdc, 0x72, 0x22, 0x5c, 0x8c, - 0xeb, 0x2b, 0x80, 0x30, 0x5a, 0x5a, 0x49, 0xe5, 0x14, 0x96, 0xe3, 0x55, 0xb9, 0xf7, 0xb3, 0xb2, - 0x39, 0x9b, 0x6a, 0x6a, 0x66, 0xbb, 0xe2, 0xef, 0x4a, 0xb4, 0x5a, 0x3f, 0xd9, 0xab, 0xe2, 0xaf, - 0xed, 0x55, 0xbb, 0xf7, 0xed, 0xaa, 0x09, 0x2e, 0xae, 0x9a, 0xe0, 0xfb, 0x55, 0x13, 0x7c, 0xb8, - 0x6e, 0x16, 0x2e, 0xae, 0x9b, 0x85, 0xcb, 0xeb, 0x66, 0xe1, 0xc5, 0xa9, 0x4b, 0x44, 0x3f, 0xb0, - 0x0d, 0x87, 0xf9, 0xa6, 0xc3, 0xb8, 0xcf, 0xb8, 0x49, 0x6c, 0x67, 0xc7, 0x65, 0xe6, 0xe8, 0xc0, - 0xf4, 0x59, 0x37, 0xf0, 0x30, 0x8f, 0xfe, 0x4d, 0x3b, 0xf3, 0x9f, 0xd3, 0xbf, 0x07, 0x3b, 0xa9, - 0xf1, 0xfe, 0x2f, 0x75, 0xb6, 0x57, 0x24, 0xc7, 0xfd, 0x1f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xdb, - 0x7e, 0xaf, 0x7a, 0xd2, 0x06, 0x00, 0x00, + // 708 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xcd, 0x6e, 0xd3, 0x4a, + 0x14, 0xce, 0xa4, 0xbe, 0x55, 0x33, 0xc9, 0x4d, 0x7b, 0xad, 0xb4, 0x4a, 0xa3, 0xab, 0x38, 0xf2, + 0xaa, 0x9b, 0xda, 0xb7, 0xad, 0xd4, 0x45, 0xef, 0x86, 0xa6, 0x15, 0x42, 0x50, 0x04, 0x72, 0xcb, + 0x86, 0x4d, 0x34, 0x76, 0x26, 0xce, 0x08, 0x7b, 0x26, 0x64, 0xc6, 0x89, 0x82, 0x58, 0xb0, 0x64, + 0xc9, 0x02, 0xf6, 0x08, 0x89, 0x77, 0x61, 0xd9, 0x65, 0x57, 0x11, 0x6a, 0x25, 0x1e, 0x20, 0x4f, + 0x80, 0x3c, 0x76, 0xe2, 0x9f, 0x96, 0x54, 0x42, 0xec, 0xe6, 0x9c, 0x39, 0x3f, 0xdf, 0xf7, 0xcd, + 0x39, 0x36, 0xdc, 0x23, 0xb6, 0x63, 0x7a, 0xc4, 0xed, 0x0b, 0xc7, 0x23, 0x98, 0x0a, 0x6e, 0x72, + 0xe6, 0x31, 0x1f, 0x39, 0x7d, 0x42, 0xb1, 0x39, 0x3a, 0x48, 0x9b, 0xc6, 0x60, 0xc8, 0x04, 0x53, + 0x35, 0x62, 0x3b, 0x46, 0x3a, 0xc5, 0x48, 0xc7, 0x8c, 0x0e, 0x1a, 0x35, 0x97, 0xb9, 0x4c, 0xc6, + 0x9a, 0xe1, 0x29, 0x4a, 0x6b, 0x6c, 0xbb, 0x8c, 0xb9, 0x1e, 0x36, 0xa5, 0x65, 0x07, 0x3d, 0x13, + 0xd1, 0x49, 0x74, 0xa5, 0x5f, 0x01, 0x58, 0x3e, 0x91, 0xb5, 0xce, 0x05, 0x12, 0x58, 0x6d, 0xc0, + 0x35, 0x8e, 0x5f, 0x07, 0x98, 0x3a, 0xb8, 0x0e, 0x5a, 0x60, 0x47, 0xb1, 0x16, 0xb6, 0xba, 0x07, + 0x4b, 0x84, 0x77, 0x7a, 0x43, 0xf6, 0x06, 0xd3, 0x7a, 0xb1, 0x05, 0x76, 0xd6, 0xda, 0xb5, 0xd9, + 0x54, 0xdb, 0x98, 0x20, 0xdf, 0x3b, 0xd2, 0x17, 0x57, 0xba, 0xb5, 0x46, 0xf8, 0x43, 0x79, 0x54, + 0x05, 0x5c, 0x77, 0x18, 0xe5, 0x98, 0xf2, 0x80, 0x77, 0x78, 0xd8, 0xa1, 0xbe, 0xd2, 0x02, 0x3b, + 0xe5, 0x7d, 0xd3, 0xb8, 0x87, 0x8a, 0x71, 0x32, 0xcf, 0x93, 0xc0, 0xda, 0x8d, 0xd9, 0x54, 0xdb, + 0x8a, 0x3a, 0xe5, 0x2a, 0xea, 0x56, 0xd5, 0xc9, 0xc4, 0x1e, 0x29, 0xef, 0x3f, 0x6b, 0x05, 0xfd, + 0x0b, 0x80, 0xd5, 0x6c, 0x11, 0xf5, 0x31, 0x84, 0x83, 0xc0, 0xf6, 0x88, 0xd3, 0x79, 0x85, 0x27, + 0x92, 0x5f, 0x79, 0xbf, 0x66, 0x44, 0xea, 0x18, 0x73, 0x75, 0x8c, 0x63, 0x3a, 0x69, 0x6f, 0xce, + 0xa6, 0xda, 0x3f, 0x51, 0xbb, 0x24, 0x43, 0xb7, 0x4a, 0x91, 0xf1, 0x04, 0x4f, 0xd4, 0x16, 0x2c, + 0x77, 0xc9, 0x08, 0x0f, 0x39, 0xe9, 0x11, 0x3c, 0x94, 0x7a, 0x94, 0xac, 0xb4, 0x4b, 0xfd, 0x17, + 0x96, 0x04, 0xf1, 0x31, 0x17, 0xc8, 0x1f, 0x48, 0xda, 0x8a, 0x95, 0x38, 0x62, 0x90, 0x3f, 0x00, + 0x5c, 0x7d, 0x84, 0x51, 0x37, 0x1f, 0x0e, 0x72, 0xe1, 0xe1, 0x2d, 0x27, 0x2e, 0x45, 0x22, 0x18, + 0x62, 0xd9, 0xac, 0x62, 0x25, 0x0e, 0xf5, 0x05, 0xac, 0x52, 0x3c, 0xee, 0xa4, 0xc8, 0xad, 0x2c, + 0x21, 0xb7, 0x3d, 0x9b, 0x6a, 0x9b, 0x11, 0xb9, 0x6c, 0x96, 0x6e, 0x55, 0x28, 0x1e, 0x3f, 0x5f, + 0x70, 0x3c, 0x81, 0xeb, 0x61, 0x40, 0x9a, 0xa7, 0x12, 0xf2, 0x4c, 0xbf, 0x46, 0x2e, 0x40, 0xb7, + 0x42, 0x24, 0xa7, 0x89, 0x23, 0x26, 0xfa, 0xb1, 0x08, 0x2b, 0x4f, 0x09, 0xb7, 0x71, 0x1f, 0x8d, + 0x08, 0x0b, 0x86, 0x4b, 0x27, 0x6d, 0x00, 0xff, 0x5e, 0x70, 0xeb, 0x30, 0x1a, 0x11, 0x2e, 0xef, + 0xef, 0xdd, 0x3b, 0x34, 0xe7, 0xf3, 0xac, 0x63, 0xda, 0x3d, 0x45, 0x02, 0xb5, 0xeb, 0xb3, 0xa9, + 0x56, 0x8b, 0x80, 0x66, 0x2a, 0xea, 0x56, 0x65, 0x61, 0x3f, 0xa3, 0xb9, 0x8e, 0x62, 0xcc, 0x62, + 0xfd, 0xfe, 0x54, 0x47, 0x31, 0x66, 0xe9, 0x8e, 0x17, 0x63, 0x16, 0xcb, 0xf2, 0x16, 0x6e, 0xe4, + 0x2b, 0x64, 0x9f, 0x1a, 0xe4, 0x9f, 0x5a, 0x85, 0xca, 0x00, 0x89, 0x7e, 0x3c, 0x03, 0xf2, 0x1c, + 0xfa, 0xba, 0x48, 0x20, 0x09, 0xba, 0x62, 0xc9, 0x73, 0x76, 0x9c, 0x94, 0xbb, 0xa7, 0xef, 0x1d, + 0x80, 0xf5, 0x8b, 0xb9, 0x0f, 0x77, 0x17, 0x48, 0x24, 0x8c, 0x07, 0xb0, 0x9a, 0x10, 0x90, 0xe5, + 0x25, 0x96, 0xf4, 0xf4, 0x64, 0xef, 0x75, 0x2b, 0xd1, 0xf0, 0xf4, 0x16, 0x84, 0xe2, 0xdd, 0x10, + 0x3e, 0x01, 0x58, 0x0a, 0xfb, 0xb6, 0x27, 0x02, 0xf3, 0xa5, 0x43, 0xb1, 0xb4, 0x5a, 0x7e, 0x1d, + 0x57, 0x6e, 0xaf, 0xe3, 0x5c, 0x38, 0xe5, 0x0e, 0xe1, 0xfe, 0x4a, 0x84, 0x8b, 0x71, 0x7d, 0x05, + 0x10, 0x46, 0x8b, 0x29, 0xa9, 0x9c, 0xc1, 0x72, 0xbc, 0x2a, 0xf7, 0x7e, 0x3a, 0xb6, 0x66, 0x53, + 0x4d, 0xcd, 0x6c, 0x57, 0xfc, 0xed, 0x88, 0x56, 0xeb, 0x17, 0x7b, 0x55, 0xfc, 0xbd, 0xbd, 0x6a, + 0xf7, 0xbe, 0x5d, 0x37, 0xc1, 0xe5, 0x75, 0x13, 0x7c, 0xbf, 0x6e, 0x82, 0x0f, 0x37, 0xcd, 0xc2, + 0xe5, 0x4d, 0xb3, 0x70, 0x75, 0xd3, 0x2c, 0xbc, 0x3c, 0x73, 0x89, 0xe8, 0x07, 0xb6, 0xe1, 0x30, + 0xdf, 0x74, 0x18, 0xf7, 0x19, 0x37, 0x89, 0xed, 0xec, 0xba, 0xcc, 0x1c, 0x1d, 0x9a, 0x3e, 0xeb, + 0x06, 0x1e, 0xe6, 0xd1, 0xff, 0x67, 0x77, 0xfe, 0x03, 0xfa, 0xef, 0x70, 0x37, 0x35, 0xde, 0xff, + 0xa7, 0xce, 0xf6, 0xaa, 0xe4, 0x78, 0xf0, 0x33, 0x00, 0x00, 0xff, 0xff, 0x01, 0xee, 0x32, 0x83, + 0xb6, 0x06, 0x00, 0x00, } func (m *ClientState) Marshal() (dAtA []byte, err error) { @@ -553,7 +551,7 @@ func (m *Header) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.NewDiversifier) i = encodeVarintSolomachine(dAtA, i, uint64(len(m.NewDiversifier))) i-- - dAtA[i] = 0x2a + dAtA[i] = 0x22 } if m.NewPublicKey != nil { { @@ -565,23 +563,18 @@ func (m *Header) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintSolomachine(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x22 + dAtA[i] = 0x1a } if len(m.Signature) > 0 { i -= len(m.Signature) copy(dAtA[i:], m.Signature) i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Signature))) i-- - dAtA[i] = 0x1a + dAtA[i] = 0x12 } if m.Timestamp != 0 { i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) i-- - dAtA[i] = 0x10 - } - if m.Sequence != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) - i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil @@ -875,9 +868,6 @@ func (m *Header) Size() (n int) { } var l int _ = l - if m.Sequence != 0 { - n += 1 + sovSolomachine(uint64(m.Sequence)) - } if m.Timestamp != 0 { n += 1 + sovSolomachine(uint64(m.Timestamp)) } @@ -1298,25 +1288,6 @@ func (m *Header) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) - } - m.Sequence = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Sequence |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) } @@ -1335,7 +1306,7 @@ func (m *Header) Unmarshal(dAtA []byte) error { break } } - case 3: + case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) } @@ -1369,7 +1340,7 @@ func (m *Header) Unmarshal(dAtA []byte) error { m.Signature = []byte{} } iNdEx = postIndex - case 4: + case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field NewPublicKey", wireType) } @@ -1405,7 +1376,7 @@ func (m *Header) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 5: + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field NewDiversifier", wireType) } diff --git a/modules/light-clients/06-solomachine/update.go b/modules/light-clients/06-solomachine/update.go index 2ccd4d74add..fc1bab93601 100644 --- a/modules/light-clients/06-solomachine/update.go +++ b/modules/light-clients/06-solomachine/update.go @@ -26,14 +26,6 @@ func (cs ClientState) VerifyClientMessage(ctx sdk.Context, cdc codec.BinaryCodec } func (cs ClientState) verifyHeader(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, header *Header) error { - // assert update sequence is current sequence - if header.Sequence != cs.Sequence { - return sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, - "header sequence does not match the client state sequence (%d != %d)", header.Sequence, cs.Sequence, - ) - } - // assert update timestamp is not less than current consensus state timestamp if header.Timestamp < cs.ConsensusState.Timestamp { return sdkerrors.Wrapf( @@ -54,7 +46,7 @@ func (cs ClientState) verifyHeader(ctx sdk.Context, cdc codec.BinaryCodec, clien } signBytes := &SignBytes{ - Sequence: header.Sequence, + Sequence: cs.Sequence, Timestamp: header.Timestamp, Diversifier: cs.ConsensusState.Diversifier, Path: []byte(SentinelHeaderPath), diff --git a/modules/light-clients/06-solomachine/update_test.go b/modules/light-clients/06-solomachine/update_test.go index a8311fbcfbf..8a251dc22cf 100644 --- a/modules/light-clients/06-solomachine/update_test.go +++ b/modules/light-clients/06-solomachine/update_test.go @@ -54,16 +54,6 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { }, false, }, - { - "wrong sequence in header", - func() { - // store in temp before assigning to interface type - h := sm.CreateHeader(sm.Diversifier) - h.Sequence++ - clientMsg = h - }, - false, - }, { "invalid header Signature", func() { @@ -458,7 +448,6 @@ func (suite *SoloMachineTestSuite) TestUpdateState() { suite.Require().Equal(newClientState.(*solomachine.ClientState).Sequence, consensusHeights[0].GetRevisionHeight()) suite.Require().False(newClientState.(*solomachine.ClientState).IsFrozen) - suite.Require().Equal(clientMsg.(*solomachine.Header).Sequence+1, newClientState.(*solomachine.ClientState).Sequence) suite.Require().Equal(clientMsg.(*solomachine.Header).NewPublicKey, newClientState.(*solomachine.ClientState).ConsensusState.PublicKey) suite.Require().Equal(clientMsg.(*solomachine.Header).NewDiversifier, newClientState.(*solomachine.ClientState).ConsensusState.Diversifier) suite.Require().Equal(clientMsg.(*solomachine.Header).Timestamp, newClientState.(*solomachine.ClientState).ConsensusState.Timestamp) diff --git a/proto/ibc/lightclients/solomachine/v3/solomachine.proto b/proto/ibc/lightclients/solomachine/v3/solomachine.proto index 8203c17796b..09bc97a9aa7 100644 --- a/proto/ibc/lightclients/solomachine/v3/solomachine.proto +++ b/proto/ibc/lightclients/solomachine/v3/solomachine.proto @@ -35,12 +35,11 @@ message ConsensusState { // Header defines a solo machine consensus header message Header { option (gogoproto.goproto_getters) = false; - // sequence to update solo machine public key at - uint64 sequence = 1; - uint64 timestamp = 2; - bytes signature = 3; - google.protobuf.Any new_public_key = 4 [(gogoproto.moretags) = "yaml:\"new_public_key\""]; - string new_diversifier = 5 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; + + uint64 timestamp = 1; + bytes signature = 2; + google.protobuf.Any new_public_key = 3 [(gogoproto.moretags) = "yaml:\"new_public_key\""]; + string new_diversifier = 4 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; } // Misbehaviour defines misbehaviour for a solo machine which consists @@ -57,18 +56,20 @@ message Misbehaviour { // signature. message SignatureAndData { option (gogoproto.goproto_getters) = false; - bytes signature = 1; - bytes path = 2; - bytes data = 3; - uint64 timestamp = 4; + + bytes signature = 1; + bytes path = 2; + bytes data = 3; + uint64 timestamp = 4; } // TimestampedSignatureData contains the signature data and the timestamp of the // signature. message TimestampedSignatureData { option (gogoproto.goproto_getters) = false; - bytes signature_data = 1 [(gogoproto.moretags) = "yaml:\"signature_data\""]; - uint64 timestamp = 2; + + bytes signature_data = 1 [(gogoproto.moretags) = "yaml:\"signature_data\""]; + uint64 timestamp = 2; } // SignBytes defines the signed bytes used for signature verification. diff --git a/testing/solomachine.go b/testing/solomachine.go index 0b996781f0a..21dcd67ebcd 100644 --- a/testing/solomachine.go +++ b/testing/solomachine.go @@ -174,7 +174,6 @@ func (solo *Solomachine) CreateHeader(newDiversifier string) *solomachine.Header sig := solo.GenerateSignature(bz) header := &solomachine.Header{ - Sequence: solo.Sequence, Timestamp: solo.Time, Signature: sig, NewPublicKey: publicKey, From 0deb8adea165c10b60c2f1d0279769314fa55b0b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 15:19:46 +0100 Subject: [PATCH 43/44] chore(deps): bump bufbuild/buf-setup-action from 1.9.0 to 1.10.0 (#2933) --- .github/workflows/proto-registry.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/proto-registry.yml b/.github/workflows/proto-registry.yml index 1aa89109785..aa617f57be1 100644 --- a/.github/workflows/proto-registry.yml +++ b/.github/workflows/proto-registry.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: bufbuild/buf-setup-action@v1.9.0 + - uses: bufbuild/buf-setup-action@v1.10.0 - uses: bufbuild/buf-push-action@v1 with: input: "proto" From 8f2bbd2894837eab419e83fb4c81828f0046b5f7 Mon Sep 17 00:00:00 2001 From: Zaki Manian Date: Mon, 19 Dec 2022 12:56:30 -0500 Subject: [PATCH 44/44] Resolve some code review comments --- modules/apps/transfer/types/transfer_authz.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/apps/transfer/types/transfer_authz.go b/modules/apps/transfer/types/transfer_authz.go index a9c861b1c51..bcf6e3ebb27 100644 --- a/modules/apps/transfer/types/transfer_authz.go +++ b/modules/apps/transfer/types/transfer_authz.go @@ -48,20 +48,20 @@ func IsAllowedAddress(ctx sdk.Context, receiver string, allowedAddrs []string) b // Accept implements Authorization.Accept. func (a TransferAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.AcceptResponse, error) { - mTransfer, ok := msg.(*MsgTransfer) + msgTransfer, ok := msg.(*MsgTransfer) if !ok { - return authz.AcceptResponse{}, sdkerrors.ErrInvalidType.Wrap("type mismatch") + return authz.AcceptResponse{}, sdkerrors.Wrap(sdkerrors.ErrInvalidType, "type mismatch") } for index, allocation := range a.Allocations { - if allocation.SourceChannel == mTransfer.SourceChannel && allocation.SourcePort == mTransfer.SourcePort { - limitLeft, isNegative := allocation.SpendLimit.SafeSub(mTransfer.Token) + if allocation.SourceChannel == msgTransfer.SourceChannel && allocation.SourcePort == msgTransfer.SourcePort { + limitLeft, isNegative := allocation.SpendLimit.SafeSub(msgTransfer.Token) if isNegative { return authz.AcceptResponse{}, sdkerrors.ErrInsufficientFunds.Wrapf("requested amount is more than spend limit") } - if !IsAllowedAddress(ctx, mTransfer.Receiver, allocation.AllowedAddresses) { - return authz.AcceptResponse{}, sdkerrors.ErrInsufficientFunds.Wrapf("not allowed address for transfer") + if !IsAllowedAddress(ctx, msgTransfer.Receiver, allocation.AllowedAddresses) { + return authz.AcceptResponse{}, sdkerrors.ErrInvalidAddress.Wrapf("not allowed address for transfer") } if limitLeft.IsZero() { @@ -85,7 +85,7 @@ func (a TransferAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.Accep }}, nil } } - return authz.AcceptResponse{}, sdkerrors.ErrInsufficientFunds.Wrapf("requested port and channel allocation does not exist") + return authz.AcceptResponse{}, sdkerrors.ErrNotFound.Wrapf("requested port and channel allocation does not exist") } // ValidateBasic implements Authorization.ValidateBasic.