From c07bca9c2381a218b61ec3872e5616b350875db7 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Mon, 8 Apr 2024 13:00:20 +0100 Subject: [PATCH 01/42] Adding proto files for ics20-v2 (#6110) * chore: adding proto files for ics20-v2 * chore: add newline --- modules/apps/transfer/types/v3/packet.pb.go | 760 ++++++++++++++++++ .../ibc/applications/transfer/v3/packet.proto | 29 + 2 files changed, 789 insertions(+) create mode 100644 modules/apps/transfer/types/v3/packet.pb.go create mode 100644 proto/ibc/applications/transfer/v3/packet.proto diff --git a/modules/apps/transfer/types/v3/packet.pb.go b/modules/apps/transfer/types/v3/packet.pb.go new file mode 100644 index 00000000000..a97568e7521 --- /dev/null +++ b/modules/apps/transfer/types/v3/packet.pb.go @@ -0,0 +1,760 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/applications/transfer/v3/packet.proto + +package v3 + +import ( + fmt "fmt" + proto "github.com/cosmos/gogoproto/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 + +// FungibleTokenPacketData defines a struct for the packet payload +// See FungibleTokenPacketData spec: +// https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#data-structures +type FungibleTokenPacketData struct { + // the tokens to be transferred + Tokens []*Token `protobuf:"bytes,1,rep,name=tokens,proto3" json:"tokens,omitempty"` + // the sender address + Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty"` + // the recipient address on the destination chain + Receiver string `protobuf:"bytes,3,opt,name=receiver,proto3" json:"receiver,omitempty"` + // optional memo + Memo string `protobuf:"bytes,4,opt,name=memo,proto3" json:"memo,omitempty"` +} + +func (m *FungibleTokenPacketData) Reset() { *m = FungibleTokenPacketData{} } +func (m *FungibleTokenPacketData) String() string { return proto.CompactTextString(m) } +func (*FungibleTokenPacketData) ProtoMessage() {} +func (*FungibleTokenPacketData) Descriptor() ([]byte, []int) { + return fileDescriptor_760742a8894acdbe, []int{0} +} +func (m *FungibleTokenPacketData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FungibleTokenPacketData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FungibleTokenPacketData.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 *FungibleTokenPacketData) XXX_Merge(src proto.Message) { + xxx_messageInfo_FungibleTokenPacketData.Merge(m, src) +} +func (m *FungibleTokenPacketData) XXX_Size() int { + return m.Size() +} +func (m *FungibleTokenPacketData) XXX_DiscardUnknown() { + xxx_messageInfo_FungibleTokenPacketData.DiscardUnknown(m) +} + +var xxx_messageInfo_FungibleTokenPacketData proto.InternalMessageInfo + +func (m *FungibleTokenPacketData) GetTokens() []*Token { + if m != nil { + return m.Tokens + } + return nil +} + +func (m *FungibleTokenPacketData) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *FungibleTokenPacketData) GetReceiver() string { + if m != nil { + return m.Receiver + } + return "" +} + +func (m *FungibleTokenPacketData) GetMemo() string { + if m != nil { + return m.Memo + } + return "" +} + +// Token defines a struct which represents a token to be transferred. +type Token struct { + // the base token denomination to be transferred + Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` + // the token amount to be transferred + Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"` + // the trace of the token + Trace []string `protobuf:"bytes,3,rep,name=trace,proto3" json:"trace,omitempty"` +} + +func (m *Token) Reset() { *m = Token{} } +func (m *Token) String() string { return proto.CompactTextString(m) } +func (*Token) ProtoMessage() {} +func (*Token) Descriptor() ([]byte, []int) { + return fileDescriptor_760742a8894acdbe, []int{1} +} +func (m *Token) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Token) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Token.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 *Token) XXX_Merge(src proto.Message) { + xxx_messageInfo_Token.Merge(m, src) +} +func (m *Token) XXX_Size() int { + return m.Size() +} +func (m *Token) XXX_DiscardUnknown() { + xxx_messageInfo_Token.DiscardUnknown(m) +} + +var xxx_messageInfo_Token proto.InternalMessageInfo + +func (m *Token) GetDenom() string { + if m != nil { + return m.Denom + } + return "" +} + +func (m *Token) GetAmount() uint64 { + if m != nil { + return m.Amount + } + return 0 +} + +func (m *Token) GetTrace() []string { + if m != nil { + return m.Trace + } + return nil +} + +func init() { + proto.RegisterType((*FungibleTokenPacketData)(nil), "ibc.applications.transfer.v3.FungibleTokenPacketData") + proto.RegisterType((*Token)(nil), "ibc.applications.transfer.v3.Token") +} + +func init() { + proto.RegisterFile("ibc/applications/transfer/v3/packet.proto", fileDescriptor_760742a8894acdbe) +} + +var fileDescriptor_760742a8894acdbe = []byte{ + // 302 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x90, 0xc1, 0x4a, 0x33, 0x31, + 0x14, 0x85, 0x9b, 0x7f, 0xda, 0xf2, 0x1b, 0x77, 0x41, 0x74, 0x10, 0x19, 0x4a, 0xdd, 0xd4, 0x85, + 0x09, 0x38, 0x1b, 0xd1, 0x9d, 0x88, 0x1b, 0x37, 0x52, 0xba, 0x72, 0x97, 0xa4, 0xd7, 0x1a, 0xda, + 0xe4, 0x0e, 0x49, 0x66, 0xc0, 0xb7, 0xf0, 0x09, 0x7c, 0x1e, 0x97, 0x5d, 0xba, 0x94, 0xf6, 0x45, + 0x64, 0xe2, 0x28, 0x5d, 0xb9, 0xbb, 0xdf, 0xbd, 0xe7, 0xdc, 0x03, 0x87, 0x9e, 0x19, 0xa5, 0x85, + 0xac, 0xaa, 0x95, 0xd1, 0x32, 0x1a, 0x74, 0x41, 0x44, 0x2f, 0x5d, 0x78, 0x02, 0x2f, 0x9a, 0x52, + 0x54, 0x52, 0x2f, 0x21, 0xf2, 0xca, 0x63, 0x44, 0x76, 0x62, 0x94, 0xe6, 0xbb, 0x52, 0xfe, 0x23, + 0xe5, 0x4d, 0x39, 0x7e, 0x23, 0xf4, 0xe8, 0xae, 0x76, 0x0b, 0xa3, 0x56, 0x30, 0xc3, 0x25, 0xb8, + 0x87, 0xe4, 0xbd, 0x95, 0x51, 0xb2, 0x6b, 0x3a, 0x8c, 0xed, 0x2a, 0xe4, 0x64, 0x94, 0x4d, 0xf6, + 0x2f, 0x4e, 0xf9, 0x5f, 0xaf, 0x78, 0xb2, 0x4f, 0x3b, 0x0b, 0x3b, 0xa4, 0xc3, 0x00, 0x6e, 0x0e, + 0x3e, 0xff, 0x37, 0x22, 0x93, 0xbd, 0x69, 0x47, 0xec, 0x98, 0xfe, 0xf7, 0xa0, 0xc1, 0x34, 0xe0, + 0xf3, 0x2c, 0x5d, 0x7e, 0x99, 0x31, 0xda, 0xb7, 0x60, 0x31, 0xef, 0xa7, 0x7d, 0x9a, 0xc7, 0xf7, + 0x74, 0x90, 0x1e, 0xb3, 0x03, 0x3a, 0x98, 0x83, 0x43, 0x9b, 0x93, 0x74, 0xfd, 0x86, 0x36, 0x46, + 0x5a, 0xac, 0x5d, 0x4c, 0x31, 0xfd, 0x69, 0x47, 0xad, 0x3a, 0x7a, 0xa9, 0x21, 0xcf, 0x46, 0x59, + 0xab, 0x4e, 0x70, 0x33, 0x7b, 0xdf, 0x14, 0x64, 0xbd, 0x29, 0xc8, 0xe7, 0xa6, 0x20, 0xaf, 0xdb, + 0xa2, 0xb7, 0xde, 0x16, 0xbd, 0x8f, 0x6d, 0xd1, 0x7b, 0xbc, 0x5a, 0x98, 0xf8, 0x5c, 0x2b, 0xae, + 0xd1, 0x0a, 0x8d, 0xc1, 0x62, 0x10, 0x46, 0xe9, 0xf3, 0x05, 0x8a, 0xe6, 0x52, 0x58, 0x9c, 0xd7, + 0x2b, 0x08, 0x6d, 0xe1, 0x3b, 0x45, 0xc7, 0x97, 0x0a, 0x82, 0x68, 0x4a, 0x35, 0x4c, 0x45, 0x97, + 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xaf, 0xbe, 0xa6, 0xe8, 0x95, 0x01, 0x00, 0x00, +} + +func (m *FungibleTokenPacketData) 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 *FungibleTokenPacketData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FungibleTokenPacketData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Memo) > 0 { + i -= len(m.Memo) + copy(dAtA[i:], m.Memo) + i = encodeVarintPacket(dAtA, i, uint64(len(m.Memo))) + i-- + dAtA[i] = 0x22 + } + if len(m.Receiver) > 0 { + i -= len(m.Receiver) + copy(dAtA[i:], m.Receiver) + i = encodeVarintPacket(dAtA, i, uint64(len(m.Receiver))) + i-- + dAtA[i] = 0x1a + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintPacket(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0x12 + } + if len(m.Tokens) > 0 { + for iNdEx := len(m.Tokens) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Tokens[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPacket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Token) 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 *Token) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Token) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Trace) > 0 { + for iNdEx := len(m.Trace) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Trace[iNdEx]) + copy(dAtA[i:], m.Trace[iNdEx]) + i = encodeVarintPacket(dAtA, i, uint64(len(m.Trace[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if m.Amount != 0 { + i = encodeVarintPacket(dAtA, i, uint64(m.Amount)) + i-- + dAtA[i] = 0x10 + } + if len(m.Denom) > 0 { + i -= len(m.Denom) + copy(dAtA[i:], m.Denom) + i = encodeVarintPacket(dAtA, i, uint64(len(m.Denom))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintPacket(dAtA []byte, offset int, v uint64) int { + offset -= sovPacket(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *FungibleTokenPacketData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Tokens) > 0 { + for _, e := range m.Tokens { + l = e.Size() + n += 1 + l + sovPacket(uint64(l)) + } + } + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovPacket(uint64(l)) + } + l = len(m.Receiver) + if l > 0 { + n += 1 + l + sovPacket(uint64(l)) + } + l = len(m.Memo) + if l > 0 { + n += 1 + l + sovPacket(uint64(l)) + } + return n +} + +func (m *Token) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Denom) + if l > 0 { + n += 1 + l + sovPacket(uint64(l)) + } + if m.Amount != 0 { + n += 1 + sovPacket(uint64(m.Amount)) + } + if len(m.Trace) > 0 { + for _, s := range m.Trace { + l = len(s) + n += 1 + l + sovPacket(uint64(l)) + } + } + return n +} + +func sovPacket(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozPacket(x uint64) (n int) { + return sovPacket(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *FungibleTokenPacketData) 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 ErrIntOverflowPacket + } + 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: FungibleTokenPacketData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FungibleTokenPacketData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tokens", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPacket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Tokens = append(m.Tokens, &Token{}) + if err := m.Tokens[len(m.Tokens)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + 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 ErrInvalidLengthPacket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Receiver", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + 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 ErrInvalidLengthPacket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Receiver = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Memo", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + 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 ErrInvalidLengthPacket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Memo = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPacket(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPacket + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Token) 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 ErrIntOverflowPacket + } + 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: Token: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Token: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + 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 ErrInvalidLengthPacket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + m.Amount = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Amount |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Trace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + 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 ErrInvalidLengthPacket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Trace = append(m.Trace, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPacket(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPacket + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipPacket(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, ErrIntOverflowPacket + } + 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, ErrIntOverflowPacket + } + 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, ErrIntOverflowPacket + } + 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, ErrInvalidLengthPacket + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupPacket + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthPacket + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthPacket = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowPacket = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupPacket = fmt.Errorf("proto: unexpected end of group") +) diff --git a/proto/ibc/applications/transfer/v3/packet.proto b/proto/ibc/applications/transfer/v3/packet.proto new file mode 100644 index 00000000000..32fc8938cd5 --- /dev/null +++ b/proto/ibc/applications/transfer/v3/packet.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package ibc.applications.transfer.v3; + +option go_package = "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3"; + +// FungibleTokenPacketData defines a struct for the packet payload +// See FungibleTokenPacketData spec: +// https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#data-structures +message FungibleTokenPacketData { + // the tokens to be transferred + repeated Token tokens = 1; + // the sender address + string sender = 2; + // the recipient address on the destination chain + string receiver = 3; + // optional memo + string memo = 4; +} + +// Token defines a struct which represents a token to be transferred. +message Token { + // the base token denomination to be transferred + string denom = 1; + // the token amount to be transferred + uint64 amount = 2; + // the trace of the token + repeated string trace = 3; +} From e66bd89038695b555e59cd6ea78d4b7d8f11bc39 Mon Sep 17 00:00:00 2001 From: Charly Date: Tue, 9 Apr 2024 10:38:50 +0200 Subject: [PATCH 02/42] update amount -> string (#6120) --- modules/apps/transfer/types/v3/packet.pb.go | 70 ++++++++++++------- .../ibc/applications/transfer/v3/packet.proto | 2 +- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/modules/apps/transfer/types/v3/packet.pb.go b/modules/apps/transfer/types/v3/packet.pb.go index a97568e7521..145b51c5eb9 100644 --- a/modules/apps/transfer/types/v3/packet.pb.go +++ b/modules/apps/transfer/types/v3/packet.pb.go @@ -102,7 +102,7 @@ type Token struct { // the base token denomination to be transferred Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` // the token amount to be transferred - Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"` + Amount string `protobuf:"bytes,2,opt,name=amount,proto3" json:"amount,omitempty"` // the trace of the token Trace []string `protobuf:"bytes,3,rep,name=trace,proto3" json:"trace,omitempty"` } @@ -147,11 +147,11 @@ func (m *Token) GetDenom() string { return "" } -func (m *Token) GetAmount() uint64 { +func (m *Token) GetAmount() string { if m != nil { return m.Amount } - return 0 + return "" } func (m *Token) GetTrace() []string { @@ -171,26 +171,26 @@ func init() { } var fileDescriptor_760742a8894acdbe = []byte{ - // 302 bytes of a gzipped FileDescriptorProto + // 301 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x90, 0xc1, 0x4a, 0x33, 0x31, 0x14, 0x85, 0x9b, 0x7f, 0xda, 0xf2, 0x1b, 0x77, 0x41, 0x74, 0x10, 0x19, 0x4a, 0xdd, 0xd4, 0x85, 0x09, 0x38, 0x1b, 0xd1, 0x9d, 0x88, 0x1b, 0x37, 0x52, 0xba, 0x72, 0x97, 0xa4, 0xd7, 0x1a, 0xda, 0xe4, 0x0e, 0x49, 0x66, 0xc0, 0xb7, 0xf0, 0x09, 0x7c, 0x1e, 0x97, 0x5d, 0xba, 0x94, 0xf6, 0x45, - 0x64, 0xe2, 0x28, 0x5d, 0xb9, 0xbb, 0xdf, 0xbd, 0xe7, 0xdc, 0x03, 0x87, 0x9e, 0x19, 0xa5, 0x85, - 0xac, 0xaa, 0x95, 0xd1, 0x32, 0x1a, 0x74, 0x41, 0x44, 0x2f, 0x5d, 0x78, 0x02, 0x2f, 0x9a, 0x52, - 0x54, 0x52, 0x2f, 0x21, 0xf2, 0xca, 0x63, 0x44, 0x76, 0x62, 0x94, 0xe6, 0xbb, 0x52, 0xfe, 0x23, - 0xe5, 0x4d, 0x39, 0x7e, 0x23, 0xf4, 0xe8, 0xae, 0x76, 0x0b, 0xa3, 0x56, 0x30, 0xc3, 0x25, 0xb8, - 0x87, 0xe4, 0xbd, 0x95, 0x51, 0xb2, 0x6b, 0x3a, 0x8c, 0xed, 0x2a, 0xe4, 0x64, 0x94, 0x4d, 0xf6, - 0x2f, 0x4e, 0xf9, 0x5f, 0xaf, 0x78, 0xb2, 0x4f, 0x3b, 0x0b, 0x3b, 0xa4, 0xc3, 0x00, 0x6e, 0x0e, - 0x3e, 0xff, 0x37, 0x22, 0x93, 0xbd, 0x69, 0x47, 0xec, 0x98, 0xfe, 0xf7, 0xa0, 0xc1, 0x34, 0xe0, - 0xf3, 0x2c, 0x5d, 0x7e, 0x99, 0x31, 0xda, 0xb7, 0x60, 0x31, 0xef, 0xa7, 0x7d, 0x9a, 0xc7, 0xf7, - 0x74, 0x90, 0x1e, 0xb3, 0x03, 0x3a, 0x98, 0x83, 0x43, 0x9b, 0x93, 0x74, 0xfd, 0x86, 0x36, 0x46, - 0x5a, 0xac, 0x5d, 0x4c, 0x31, 0xfd, 0x69, 0x47, 0xad, 0x3a, 0x7a, 0xa9, 0x21, 0xcf, 0x46, 0x59, - 0xab, 0x4e, 0x70, 0x33, 0x7b, 0xdf, 0x14, 0x64, 0xbd, 0x29, 0xc8, 0xe7, 0xa6, 0x20, 0xaf, 0xdb, - 0xa2, 0xb7, 0xde, 0x16, 0xbd, 0x8f, 0x6d, 0xd1, 0x7b, 0xbc, 0x5a, 0x98, 0xf8, 0x5c, 0x2b, 0xae, - 0xd1, 0x0a, 0x8d, 0xc1, 0x62, 0x10, 0x46, 0xe9, 0xf3, 0x05, 0x8a, 0xe6, 0x52, 0x58, 0x9c, 0xd7, - 0x2b, 0x08, 0x6d, 0xe1, 0x3b, 0x45, 0xc7, 0x97, 0x0a, 0x82, 0x68, 0x4a, 0x35, 0x4c, 0x45, 0x97, - 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xaf, 0xbe, 0xa6, 0xe8, 0x95, 0x01, 0x00, 0x00, + 0x64, 0xd2, 0x56, 0xba, 0x72, 0x77, 0xbf, 0x7b, 0xcf, 0xb9, 0x07, 0x0e, 0xbd, 0x30, 0x4a, 0x0b, + 0x59, 0x55, 0x0b, 0xa3, 0x65, 0x34, 0xe8, 0x82, 0x88, 0x5e, 0xba, 0xf0, 0x02, 0x5e, 0x34, 0xa5, + 0xa8, 0xa4, 0x9e, 0x43, 0xe4, 0x95, 0xc7, 0x88, 0xec, 0xcc, 0x28, 0xcd, 0xf7, 0xa5, 0x7c, 0x27, + 0xe5, 0x4d, 0x39, 0xfc, 0x20, 0xf4, 0xe4, 0xa1, 0x76, 0x33, 0xa3, 0x16, 0x30, 0xc1, 0x39, 0xb8, + 0xa7, 0xe4, 0xbd, 0x97, 0x51, 0xb2, 0x5b, 0xda, 0x8f, 0xed, 0x2a, 0xe4, 0x64, 0x90, 0x8d, 0x0e, + 0xaf, 0xce, 0xf9, 0x5f, 0xaf, 0x78, 0xb2, 0x8f, 0xb7, 0x16, 0x76, 0x4c, 0xfb, 0x01, 0xdc, 0x14, + 0x7c, 0xfe, 0x6f, 0x40, 0x46, 0x07, 0xe3, 0x2d, 0xb1, 0x53, 0xfa, 0xdf, 0x83, 0x06, 0xd3, 0x80, + 0xcf, 0xb3, 0x74, 0xf9, 0x65, 0xc6, 0x68, 0xd7, 0x82, 0xc5, 0xbc, 0x9b, 0xf6, 0x69, 0x1e, 0x3e, + 0xd2, 0x5e, 0x7a, 0xcc, 0x8e, 0x68, 0x6f, 0x0a, 0x0e, 0x6d, 0x4e, 0xd2, 0x75, 0x03, 0x6d, 0x8c, + 0xb4, 0x58, 0xbb, 0xb8, 0x8b, 0xd9, 0x50, 0xab, 0x8e, 0x5e, 0x6a, 0xc8, 0xb3, 0x41, 0xd6, 0xaa, + 0x13, 0xdc, 0x4d, 0x3e, 0x57, 0x05, 0x59, 0xae, 0x0a, 0xf2, 0xbd, 0x2a, 0xc8, 0xfb, 0xba, 0xe8, + 0x2c, 0xd7, 0x45, 0xe7, 0x6b, 0x5d, 0x74, 0x9e, 0x6f, 0x66, 0x26, 0xbe, 0xd6, 0x8a, 0x6b, 0xb4, + 0x42, 0x63, 0xb0, 0x18, 0x84, 0x51, 0xfa, 0x72, 0x86, 0xa2, 0xb9, 0x16, 0x16, 0xa7, 0xf5, 0x02, + 0x42, 0x5b, 0xf8, 0x5e, 0xd1, 0xf1, 0xad, 0x82, 0x20, 0x9a, 0x52, 0xf5, 0x53, 0xd1, 0xe5, 0x4f, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x97, 0x4d, 0xcc, 0xde, 0x95, 0x01, 0x00, 0x00, } func (m *FungibleTokenPacketData) Marshal() (dAtA []byte, err error) { @@ -280,10 +280,12 @@ func (m *Token) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x1a } } - if m.Amount != 0 { - i = encodeVarintPacket(dAtA, i, uint64(m.Amount)) + if len(m.Amount) > 0 { + i -= len(m.Amount) + copy(dAtA[i:], m.Amount) + i = encodeVarintPacket(dAtA, i, uint64(len(m.Amount))) i-- - dAtA[i] = 0x10 + dAtA[i] = 0x12 } if len(m.Denom) > 0 { i -= len(m.Denom) @@ -343,8 +345,9 @@ func (m *Token) Size() (n int) { if l > 0 { n += 1 + l + sovPacket(uint64(l)) } - if m.Amount != 0 { - n += 1 + sovPacket(uint64(m.Amount)) + l = len(m.Amount) + if l > 0 { + n += 1 + l + sovPacket(uint64(l)) } if len(m.Trace) > 0 { for _, s := range m.Trace { @@ -603,10 +606,10 @@ func (m *Token) Unmarshal(dAtA []byte) error { m.Denom = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: - if wireType != 0 { + if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) } - m.Amount = 0 + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowPacket @@ -616,11 +619,24 @@ func (m *Token) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Amount |= uint64(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPacket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Amount = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Trace", wireType) diff --git a/proto/ibc/applications/transfer/v3/packet.proto b/proto/ibc/applications/transfer/v3/packet.proto index 32fc8938cd5..8971472c69d 100644 --- a/proto/ibc/applications/transfer/v3/packet.proto +++ b/proto/ibc/applications/transfer/v3/packet.proto @@ -23,7 +23,7 @@ message Token { // the base token denomination to be transferred string denom = 1; // the token amount to be transferred - uint64 amount = 2; + string amount = 2; // the trace of the token repeated string trace = 3; } From 034f47210dca7bfe4a74ba9af6675cb901a64095 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Tue, 9 Apr 2024 12:52:03 +0100 Subject: [PATCH 03/42] Update MsgTransfer to accept sdk.Coins instead of sdk.Coin (#6113) --- e2e/tests/transfer/authz_test.go | 88 ++++++----- e2e/tests/transfer/incentivized_test.go | 4 +- e2e/tests/transfer/upgrades_test.go | 4 +- e2e/tests/upgrades/upgrade_test.go | 2 +- e2e/testsuite/tx.go | 2 +- e2e/testvalues/values.go | 4 + .../host/keeper/relay_test.go | 38 ++--- modules/apps/29-fee/keeper/events_test.go | 2 +- modules/apps/29-fee/transfer_test.go | 4 +- modules/apps/callbacks/ibc_middleware_test.go | 2 +- modules/apps/callbacks/replay_test.go | 2 +- modules/apps/callbacks/transfer_test.go | 4 +- modules/apps/transfer/client/cli/tx.go | 2 +- .../apps/transfer/keeper/invariants_test.go | 2 +- .../apps/transfer/keeper/mbt_relay_test.go | 2 +- modules/apps/transfer/keeper/msg_server.go | 15 +- .../apps/transfer/keeper/msg_server_test.go | 2 +- modules/apps/transfer/keeper/relay_test.go | 12 +- modules/apps/transfer/transfer_test.go | 6 +- modules/apps/transfer/types/msgs.go | 59 +++++++- modules/apps/transfer/types/msgs_test.go | 48 +++--- .../transfer/types/transfer_authorization.go | 7 +- .../types/transfer_authorization_test.go | 22 +-- modules/apps/transfer/types/tx.pb.go | 137 +++++++++++++----- proto/ibc/applications/transfer/v1/tx.proto | 2 + 25 files changed, 302 insertions(+), 170 deletions(-) diff --git a/e2e/tests/transfer/authz_test.go b/e2e/tests/transfer/authz_test.go index dd390ad3da5..33297e0766d 100644 --- a/e2e/tests/transfer/authz_test.go +++ b/e2e/tests/transfer/authz_test.go @@ -104,16 +104,18 @@ func (suite *AuthzTransferTestSuite) TestAuthz_MsgTransfer_Succeeds() { t.Run("broadcast MsgGrant", createMsgGrantFn) t.Run("broadcast MsgExec for ibc MsgTransfer", func(t *testing.T) { - transferMsg := transfertypes.MsgTransfer{ - SourcePort: channelA.PortID, - SourceChannel: channelA.ChannelID, - Token: testvalues.DefaultTransferAmount(chainADenom), - Sender: granterAddress, - Receiver: receiverWalletAddress, - TimeoutHeight: suite.GetTimeoutHeight(ctx, chainB), - } - - protoAny, err := codectypes.NewAnyWithValue(&transferMsg) + transferMsg := transfertypes.NewMsgTransfer( + channelA.PortID, + channelA.ChannelID, + testvalues.DefaultTransferCoins(chainADenom), + granterAddress, + receiverWalletAddress, + suite.GetTimeoutHeight(ctx, chainB), + 0, + "", + ) + + protoAny, err := codectypes.NewAnyWithValue(transferMsg) suite.Require().NoError(err) msgExec := &authz.MsgExec{ @@ -161,16 +163,18 @@ func (suite *AuthzTransferTestSuite) TestAuthz_MsgTransfer_Succeeds() { }) t.Run("exec unauthorized MsgTransfer", func(t *testing.T) { - transferMsg := transfertypes.MsgTransfer{ - SourcePort: channelA.PortID, - SourceChannel: channelA.ChannelID, - Token: testvalues.DefaultTransferAmount(chainADenom), - Sender: granterAddress, - Receiver: receiverWalletAddress, - TimeoutHeight: suite.GetTimeoutHeight(ctx, chainB), - } - - protoAny, err := codectypes.NewAnyWithValue(&transferMsg) + transferMsg := transfertypes.NewMsgTransfer( + channelA.PortID, + channelA.ChannelID, + testvalues.DefaultTransferCoins(chainADenom), + granterAddress, + receiverWalletAddress, + suite.GetTimeoutHeight(ctx, chainB), + 0, + "", + ) + + protoAny, err := codectypes.NewAnyWithValue(transferMsg) suite.Require().NoError(err) msgExec := &authz.MsgExec{ @@ -241,16 +245,18 @@ func (suite *AuthzTransferTestSuite) TestAuthz_InvalidTransferAuthorizations() { const invalidSpendAmount = spendLimit + 1 t.Run("broadcast MsgExec for ibc MsgTransfer", func(t *testing.T) { - transferMsg := transfertypes.MsgTransfer{ - SourcePort: channelA.PortID, - SourceChannel: channelA.ChannelID, - Token: sdk.Coin{Denom: chainADenom, Amount: sdkmath.NewInt(invalidSpendAmount)}, - Sender: granterAddress, - Receiver: receiverWalletAddress, - TimeoutHeight: suite.GetTimeoutHeight(ctx, chainB), - } - - protoAny, err := codectypes.NewAnyWithValue(&transferMsg) + transferMsg := transfertypes.NewMsgTransfer( + channelA.PortID, + channelA.ChannelID, + sdk.NewCoins(sdk.Coin{Denom: chainADenom, Amount: sdkmath.NewInt(invalidSpendAmount)}), + granterAddress, + receiverWalletAddress, + suite.GetTimeoutHeight(ctx, chainB), + 0, + "", + ) + + protoAny, err := codectypes.NewAnyWithValue(transferMsg) suite.Require().NoError(err) msgExec := &authz.MsgExec{ @@ -298,16 +304,18 @@ func (suite *AuthzTransferTestSuite) TestAuthz_InvalidTransferAuthorizations() { invalidWalletAddress := invalidWallet.FormattedAddress() t.Run("broadcast MsgExec for ibc MsgTransfer", func(t *testing.T) { - transferMsg := transfertypes.MsgTransfer{ - SourcePort: channelA.PortID, - SourceChannel: channelA.ChannelID, - Token: sdk.Coin{Denom: chainADenom, Amount: sdkmath.NewInt(spendLimit)}, - Sender: granterAddress, - Receiver: invalidWalletAddress, - TimeoutHeight: suite.GetTimeoutHeight(ctx, chainB), - } - - protoAny, err := codectypes.NewAnyWithValue(&transferMsg) + transferMsg := transfertypes.NewMsgTransfer( + channelA.PortID, + channelA.ChannelID, + sdk.NewCoins(sdk.Coin{Denom: chainADenom, Amount: sdkmath.NewInt(spendLimit)}), + granterAddress, + invalidWalletAddress, + suite.GetTimeoutHeight(ctx, chainB), + 0, + "", + ) + + protoAny, err := codectypes.NewAnyWithValue(transferMsg) suite.Require().NoError(err) msgExec := &authz.MsgExec{ diff --git a/e2e/tests/transfer/incentivized_test.go b/e2e/tests/transfer/incentivized_test.go index 1902c9e0c66..b8818a00002 100644 --- a/e2e/tests/transfer/incentivized_test.go +++ b/e2e/tests/transfer/incentivized_test.go @@ -197,7 +197,7 @@ func (s *IncentivizedTransferTestSuite) TestMsgPayPacketFee_InvalidReceiverAccou transferAmount := testvalues.DefaultTransferAmount(chainADenom) t.Run("send IBC transfer", func(t *testing.T) { - msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.FormattedAddress(), testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, sdk.NewCoins(transferAmount), chainAWallet.FormattedAddress(), testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") txResp := s.BroadcastMessages(ctx, chainA, chainAWallet, msgTransfer) // this message should be successful, as receiver account is not validated on the sending chain. s.AssertTxSuccess(txResp) @@ -322,7 +322,7 @@ func (s *IncentivizedTransferTestSuite) TestMultiMsg_MsgPayPacketFeeSingleSender }) msgPayPacketFee := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.FormattedAddress(), nil) - msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") + msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, sdk.NewCoins(transferAmount), chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") resp := s.BroadcastMessages(ctx, chainA, chainAWallet, msgPayPacketFee, msgTransfer) s.AssertTxSuccess(resp) diff --git a/e2e/tests/transfer/upgrades_test.go b/e2e/tests/transfer/upgrades_test.go index c1bd43898dd..74f5593d611 100644 --- a/e2e/tests/transfer/upgrades_test.go +++ b/e2e/tests/transfer/upgrades_test.go @@ -13,6 +13,8 @@ import ( sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ibc-go/e2e/testsuite" "github.com/cosmos/ibc-go/e2e/testvalues" feetypes "github.com/cosmos/ibc-go/v8/modules/apps/29-fee/types" @@ -195,7 +197,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_ transferAmount := testvalues.DefaultTransferAmount(chainA.Config().Denom) msgPayPacketFee := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.FormattedAddress(), nil) - msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") + msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, sdk.NewCoins(transferAmount), chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") resp := s.BroadcastMessages(ctx, chainA, chainAWallet, msgPayPacketFee, msgTransfer) s.AssertTxSuccess(resp) }) diff --git a/e2e/tests/upgrades/upgrade_test.go b/e2e/tests/upgrades/upgrade_test.go index c3d56fc4f76..494a73a4511 100644 --- a/e2e/tests/upgrades/upgrade_test.go +++ b/e2e/tests/upgrades/upgrade_test.go @@ -960,7 +960,7 @@ func (s *UpgradeTestSuite) TestV8ToV8_1ChainUpgrade_ChannelUpgrades() { transferAmount := testvalues.DefaultTransferAmount(chainA.Config().Denom) msgPayPacketFee := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.FormattedAddress(), nil) - msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") + msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, sdk.NewCoins(transferAmount), chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") resp := s.BroadcastMessages(ctx, chainA, chainAWallet, msgPayPacketFee, msgTransfer) s.AssertTxSuccess(resp) }) diff --git a/e2e/testsuite/tx.go b/e2e/testsuite/tx.go index ecbde19b3ad..b0ee8ab763b 100644 --- a/e2e/testsuite/tx.go +++ b/e2e/testsuite/tx.go @@ -265,7 +265,7 @@ func (s *E2ETestSuite) ExecuteGovV1Beta1Proposal(ctx context.Context, chain ibc. func (s *E2ETestSuite) Transfer(ctx context.Context, chain ibc.Chain, user ibc.Wallet, portID, channelID string, token sdk.Coin, sender, receiver string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, memo string, ) sdk.TxResponse { - msg := transfertypes.NewMsgTransfer(portID, channelID, token, sender, receiver, timeoutHeight, timeoutTimestamp, memo) + msg := transfertypes.NewMsgTransfer(portID, channelID, sdk.NewCoins(token), sender, receiver, timeoutHeight, timeoutTimestamp, memo) return s.BroadcastMessages(ctx, chain, user, msg) } diff --git a/e2e/testvalues/values.go b/e2e/testvalues/values.go index b6f750ef7f7..07f262296f6 100644 --- a/e2e/testvalues/values.go +++ b/e2e/testvalues/values.go @@ -43,6 +43,10 @@ func DefaultTransferAmount(denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(IBCTransferAmount)} } +func DefaultTransferCoins(denom string) sdk.Coins { + return sdk.NewCoins(DefaultTransferAmount(denom)) +} + func TransferAmount(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } diff --git a/modules/apps/27-interchain-accounts/host/keeper/relay_test.go b/modules/apps/27-interchain-accounts/host/keeper/relay_test.go index ac5d41378e3..f49e623e939 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/relay_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/relay_test.go @@ -342,15 +342,16 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID) suite.Require().True(found) - msg := &transfertypes.MsgTransfer{ - SourcePort: transferPath.EndpointA.ChannelConfig.PortID, - SourceChannel: transferPath.EndpointA.ChannelID, - Token: sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100)), - Sender: interchainAccountAddr, - Receiver: suite.chainA.SenderAccount.GetAddress().String(), - TimeoutHeight: suite.chainB.GetTimeoutHeight(), - TimeoutTimestamp: uint64(0), - } + msg := transfertypes.NewMsgTransfer( + transferPath.EndpointA.ChannelConfig.PortID, + transferPath.EndpointA.ChannelID, + sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100))), + interchainAccountAddr, + suite.chainA.SenderAccount.GetAddress().String(), + suite.chainB.GetTimeoutHeight(), + 0, + "", + ) data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, encoding) suite.Require().NoError(err) @@ -376,15 +377,16 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID) suite.Require().True(found) - msg := &transfertypes.MsgTransfer{ - SourcePort: transferPath.EndpointA.ChannelConfig.PortID, - SourceChannel: transferPath.EndpointA.ChannelID, - Token: sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100)), - Sender: interchainAccountAddr, - Receiver: "", - TimeoutHeight: suite.chainB.GetTimeoutHeight(), - TimeoutTimestamp: uint64(0), - } + msg := transfertypes.NewMsgTransfer( + transferPath.EndpointA.ChannelConfig.PortID, + transferPath.EndpointA.ChannelID, + sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100))), + interchainAccountAddr, + "", + suite.chainB.GetTimeoutHeight(), + 0, + "", + ) data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, encoding) suite.Require().NoError(err) diff --git a/modules/apps/29-fee/keeper/events_test.go b/modules/apps/29-fee/keeper/events_test.go index f4f49abcf0d..d70b8bfd229 100644 --- a/modules/apps/29-fee/keeper/events_test.go +++ b/modules/apps/29-fee/keeper/events_test.go @@ -113,7 +113,7 @@ func (suite *KeeperTestSuite) TestDistributeFeeEvent() { msgTransfer := transfertypes.NewMsgTransfer( path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, - sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100)), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), + sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100))), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0, "", ) diff --git a/modules/apps/29-fee/transfer_test.go b/modules/apps/29-fee/transfer_test.go index 79a0405fc0d..f76393bfcf8 100644 --- a/modules/apps/29-fee/transfer_test.go +++ b/modules/apps/29-fee/transfer_test.go @@ -30,7 +30,7 @@ func (suite *FeeTestSuite) TestFeeTransfer() { msgs := []sdk.Msg{ types.NewMsgPayPacketFee(fee, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, suite.chainA.SenderAccount.GetAddress().String(), nil), - transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0, ""), + transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.NewCoins(coin), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0, ""), } res, err := suite.chainA.SendMsgs(msgs...) suite.Require().NoError(err) // message committed @@ -138,7 +138,7 @@ func (suite *FeeTestSuite) TestTransferFeeUpgrade() { fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) msgs := []sdk.Msg{ types.NewMsgPayPacketFee(fee, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, suite.chainA.SenderAccount.GetAddress().String(), nil), - transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, ibctesting.TestCoin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0, ""), + transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.NewCoins(ibctesting.TestCoin), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0, ""), } res, err := suite.chainA.SendMsgs(msgs...) diff --git a/modules/apps/callbacks/ibc_middleware_test.go b/modules/apps/callbacks/ibc_middleware_test.go index d041ea61b2c..180ca3eb056 100644 --- a/modules/apps/callbacks/ibc_middleware_test.go +++ b/modules/apps/callbacks/ibc_middleware_test.go @@ -463,7 +463,7 @@ func (s *CallbacksTestSuite) TestOnTimeoutPacket() { timeoutTimestamp := uint64(s.chainB.GetContext().BlockTime().UnixNano()) msg := transfertypes.NewMsgTransfer( s.path.EndpointA.ChannelConfig.PortID, s.path.EndpointA.ChannelID, - ibctesting.TestCoin, s.chainA.SenderAccount.GetAddress().String(), + sdk.NewCoins(ibctesting.TestCoin), s.chainA.SenderAccount.GetAddress().String(), s.chainB.SenderAccount.GetAddress().String(), clienttypes.ZeroHeight(), timeoutTimestamp, fmt.Sprintf(`{"src_callback": {"address":"%s", "gas_limit":"%d"}}`, ibctesting.TestAccAddress, userGasLimit), // set user gas limit above panic level in mock contract keeper ) diff --git a/modules/apps/callbacks/replay_test.go b/modules/apps/callbacks/replay_test.go index d23de002613..04a8e5900fb 100644 --- a/modules/apps/callbacks/replay_test.go +++ b/modules/apps/callbacks/replay_test.go @@ -326,7 +326,7 @@ func (s *CallbacksTestSuite) ExecuteFailedTransfer(memo string) { msg := transfertypes.NewMsgTransfer( s.path.EndpointA.ChannelConfig.PortID, s.path.EndpointA.ChannelID, - amount, + sdk.NewCoins(amount), s.chainA.SenderAccount.GetAddress().String(), s.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0, memo, diff --git a/modules/apps/callbacks/transfer_test.go b/modules/apps/callbacks/transfer_test.go index 4d288e96bbc..16698f074e6 100644 --- a/modules/apps/callbacks/transfer_test.go +++ b/modules/apps/callbacks/transfer_test.go @@ -189,7 +189,7 @@ func (s *CallbacksTestSuite) ExecuteTransfer(memo string) { msg := transfertypes.NewMsgTransfer( s.path.EndpointA.ChannelConfig.PortID, s.path.EndpointA.ChannelID, - amount, + sdk.NewCoins(amount), s.chainA.SenderAccount.GetAddress().String(), s.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0, memo, @@ -223,7 +223,7 @@ func (s *CallbacksTestSuite) ExecuteTransferTimeout(memo string) { msg := transfertypes.NewMsgTransfer( s.path.EndpointA.ChannelConfig.PortID, s.path.EndpointA.ChannelID, - amount, + sdk.NewCoins(amount), s.chainA.SenderAccount.GetAddress().String(), s.chainB.SenderAccount.GetAddress().String(), timeoutHeight, timeoutTimestamp, memo, diff --git a/modules/apps/transfer/client/cli/tx.go b/modules/apps/transfer/client/cli/tx.go index 497e56370d5..ccdd08af588 100644 --- a/modules/apps/transfer/client/cli/tx.go +++ b/modules/apps/transfer/client/cli/tx.go @@ -107,7 +107,7 @@ Relative timeout timestamp is added to the value of the user's local system cloc } msg := types.NewMsgTransfer( - srcPort, srcChannel, coin, sender, receiver, timeoutHeight, timeoutTimestamp, memo, + srcPort, srcChannel, sdk.NewCoins(coin), sender, receiver, timeoutHeight, timeoutTimestamp, memo, ) return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, diff --git a/modules/apps/transfer/keeper/invariants_test.go b/modules/apps/transfer/keeper/invariants_test.go index ef1b6ae90ca..e150f9ef7c9 100644 --- a/modules/apps/transfer/keeper/invariants_test.go +++ b/modules/apps/transfer/keeper/invariants_test.go @@ -47,7 +47,7 @@ func (suite *KeeperTestSuite) TestTotalEscrowPerDenomInvariant() { msg := types.NewMsgTransfer( path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, - coin, + sdk.NewCoins(coin), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.GetTimeoutHeight(), 0, "", diff --git a/modules/apps/transfer/keeper/mbt_relay_test.go b/modules/apps/transfer/keeper/mbt_relay_test.go index fcb466dd468..72c5f0b1f41 100644 --- a/modules/apps/transfer/keeper/mbt_relay_test.go +++ b/modules/apps/transfer/keeper/mbt_relay_test.go @@ -348,7 +348,7 @@ func (suite *KeeperTestSuite) TestModelBasedRelay() { msg := types.NewMsgTransfer( tc.packet.SourcePort, tc.packet.SourceChannel, - sdk.NewCoin(denom, amount), + sdk.NewCoins(sdk.NewCoin(denom, amount)), sender.String(), tc.packet.Data.Receiver, suite.chainA.GetTimeoutHeight(), 0, // only use timeout height diff --git a/modules/apps/transfer/keeper/msg_server.go b/modules/apps/transfer/keeper/msg_server.go index c5171573226..94536cba937 100644 --- a/modules/apps/transfer/keeper/msg_server.go +++ b/modules/apps/transfer/keeper/msg_server.go @@ -26,8 +26,11 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types. return nil, err } - if !k.bankKeeper.IsSendEnabledCoin(ctx, msg.Token) { - return nil, errorsmod.Wrapf(types.ErrSendDisabled, "%s transfers are currently disabled", msg.Token.Denom) + // TODO: replace with correct usage. + token := msg.GetTokens()[0] + + if !k.bankKeeper.IsSendEnabledCoin(ctx, token) { + return nil, errorsmod.Wrapf(types.ErrSendDisabled, "%s transfers are currently disabled", token.Denom) } if k.bankKeeper.BlockedAddr(sender) { @@ -35,21 +38,21 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types. } sequence, err := k.sendTransfer( - ctx, msg.SourcePort, msg.SourceChannel, msg.Token, sender, msg.Receiver, msg.TimeoutHeight, msg.TimeoutTimestamp, + ctx, msg.SourcePort, msg.SourceChannel, token, sender, msg.Receiver, msg.TimeoutHeight, msg.TimeoutTimestamp, msg.Memo) if err != nil { return nil, err } - k.Logger(ctx).Info("IBC fungible token transfer", "token", msg.Token.Denom, "amount", msg.Token.Amount.String(), "sender", msg.Sender, "receiver", msg.Receiver) + k.Logger(ctx).Info("IBC fungible token transfer", "token", token.Denom, "amount", token.Amount.String(), "sender", msg.Sender, "receiver", msg.Receiver) ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeTransfer, sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender), sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver), - sdk.NewAttribute(types.AttributeKeyAmount, msg.Token.Amount.String()), - sdk.NewAttribute(types.AttributeKeyDenom, msg.Token.Denom), + sdk.NewAttribute(types.AttributeKeyAmount, token.Amount.String()), + sdk.NewAttribute(types.AttributeKeyDenom, token.Denom), sdk.NewAttribute(types.AttributeKeyMemo, msg.Memo), ), sdk.NewEvent( diff --git a/modules/apps/transfer/keeper/msg_server_test.go b/modules/apps/transfer/keeper/msg_server_test.go index 55bc27a8a89..957ccf82c94 100644 --- a/modules/apps/transfer/keeper/msg_server_test.go +++ b/modules/apps/transfer/keeper/msg_server_test.go @@ -95,7 +95,7 @@ func (suite *KeeperTestSuite) TestMsgTransfer() { msg = types.NewMsgTransfer( path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, - coin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), + sdk.NewCoins(coin), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), suite.chainB.GetTimeoutHeight(), 0, // only use timeout height "memo", ) diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index 1eb02bba9bd..24ab6f9eb69 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -126,7 +126,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() { expEscrowAmount = sdkmath.ZeroInt() // create IBC token on chainA - transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coin, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainA.GetTimeoutHeight(), 0, "") + transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, sdk.NewCoins(coin), suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainA.GetTimeoutHeight(), 0, "") result, err := suite.chainB.SendMsgs(transferMsg) suite.Require().NoError(err) // message committed @@ -141,7 +141,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() { msg := types.NewMsgTransfer( path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, - coin, sender.String(), suite.chainB.SenderAccount.GetAddress().String(), + sdk.NewCoins(coin), sender.String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, // only use timeout height memo, ) @@ -206,7 +206,7 @@ func (suite *KeeperTestSuite) TestSendTransferSetsTotalEscrowAmountForSourceIBCT transferMsg := types.NewMsgTransfer( path1.EndpointA.ChannelConfig.PortID, path1.EndpointA.ChannelID, - coin, + sdk.NewCoins(coin), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), suite.chainB.GetTimeoutHeight(), 0, "", @@ -226,7 +226,7 @@ func (suite *KeeperTestSuite) TestSendTransferSetsTotalEscrowAmountForSourceIBCT msg := types.NewMsgTransfer( path2.EndpointB.ChannelConfig.PortID, path2.EndpointB.ChannelID, - coin, + sdk.NewCoins(coin), suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainA.GetTimeoutHeight(), 0, "", @@ -383,7 +383,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { if tc.recvIsSource { // send coin from chainB to chainA, receive them, acknowledge them, and send back to chainB coinFromBToA := sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100)) - transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 110), 0, memo) + transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, sdk.NewCoins(coinFromBToA), suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 110), 0, memo) res, err := suite.chainB.SendMsgs(transferMsg) suite.Require().NoError(err) // message committed @@ -403,7 +403,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { // send coin from chainA to chainB coin := sdk.NewCoin(trace.IBCDenom(), amount) - transferMsg := types.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coin, suite.chainA.SenderAccount.GetAddress().String(), receiver, clienttypes.NewHeight(1, 110), 0, memo) + transferMsg := types.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.NewCoins(coin), suite.chainA.SenderAccount.GetAddress().String(), receiver, clienttypes.NewHeight(1, 110), 0, memo) _, err := suite.chainA.SendMsgs(transferMsg) suite.Require().NoError(err) // message committed diff --git a/modules/apps/transfer/transfer_test.go b/modules/apps/transfer/transfer_test.go index ef8dea295c6..1cb1261c0e3 100644 --- a/modules/apps/transfer/transfer_test.go +++ b/modules/apps/transfer/transfer_test.go @@ -52,7 +52,7 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() { coinToSendToB := sdk.NewCoin(sdk.DefaultBondDenom, amount) // send from chainA to chainB - msg := types.NewMsgTransfer(pathAtoB.EndpointA.ChannelConfig.PortID, pathAtoB.EndpointA.ChannelID, coinToSendToB, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") + msg := types.NewMsgTransfer(pathAtoB.EndpointA.ChannelConfig.PortID, pathAtoB.EndpointA.ChannelID, sdk.NewCoins(coinToSendToB), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") res, err := suite.chainA.SendMsgs(msg) suite.Require().NoError(err) // message committed @@ -82,7 +82,7 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() { pathBtoC.Setup() // send from chainB to chainC - msg = types.NewMsgTransfer(pathBtoC.EndpointA.ChannelConfig.PortID, pathBtoC.EndpointA.ChannelID, coinSentFromAToB, suite.chainB.SenderAccount.GetAddress().String(), suite.chainC.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") + msg = types.NewMsgTransfer(pathBtoC.EndpointA.ChannelConfig.PortID, pathBtoC.EndpointA.ChannelID, sdk.NewCoins(coinSentFromAToB), suite.chainB.SenderAccount.GetAddress().String(), suite.chainC.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") res, err = suite.chainB.SendMsgs(msg) suite.Require().NoError(err) // message committed @@ -105,7 +105,7 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() { suite.Require().Zero(balance.Amount.Int64()) // send from chainC back to chainB - msg = types.NewMsgTransfer(pathBtoC.EndpointB.ChannelConfig.PortID, pathBtoC.EndpointB.ChannelID, coinSentFromBToC, suite.chainC.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") + msg = types.NewMsgTransfer(pathBtoC.EndpointB.ChannelConfig.PortID, pathBtoC.EndpointB.ChannelID, sdk.NewCoins(coinSentFromBToC), suite.chainC.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") res, err = suite.chainC.SendMsgs(msg) suite.Require().NoError(err) // message committed diff --git a/modules/apps/transfer/types/msgs.go b/modules/apps/transfer/types/msgs.go index 2b045fee47e..2994e841951 100644 --- a/modules/apps/transfer/types/msgs.go +++ b/modules/apps/transfer/types/msgs.go @@ -45,19 +45,19 @@ func (msg MsgUpdateParams) ValidateBasic() error { // NewMsgTransfer creates a new MsgTransfer instance func NewMsgTransfer( sourcePort, sourceChannel string, - token sdk.Coin, sender, receiver string, + tokens sdk.Coins, sender, receiver string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, memo string, ) *MsgTransfer { return &MsgTransfer{ SourcePort: sourcePort, SourceChannel: sourceChannel, - Token: token, Sender: sender, Receiver: receiver, TimeoutHeight: timeoutHeight, TimeoutTimestamp: timeoutTimestamp, Memo: memo, + Tokens: tokens, } } @@ -72,11 +72,13 @@ func (msg MsgTransfer) ValidateBasic() error { if err := host.ChannelIdentifierValidator(msg.SourceChannel); err != nil { return errorsmod.Wrap(err, "invalid source channel ID") } - if !msg.Token.IsValid() { - return errorsmod.Wrap(ibcerrors.ErrInvalidCoins, msg.Token.String()) + + if len(msg.Tokens) == 0 && !isValidToken(msg.Token) { + return errorsmod.Wrap(ErrInvalidAmount, "either token or token array must be filled") } - if !msg.Token.IsPositive() { - return errorsmod.Wrap(ibcerrors.ErrInsufficientFunds, msg.Token.String()) + + if len(msg.Tokens) != 0 && isValidToken(msg.Token) { + return errorsmod.Wrap(ErrInvalidAmount, "cannot fill both token and token array") } _, err := sdk.AccAddressFromBech32(msg.Sender) @@ -92,5 +94,48 @@ func (msg MsgTransfer) ValidateBasic() error { if len(msg.Memo) > MaximumMemoLength { return errorsmod.Wrapf(ErrInvalidMemo, "memo must not exceed %d bytes", MaximumMemoLength) } - return ValidateIBCDenom(msg.Token.Denom) + + for _, token := range msg.GetTokens() { + if !isValidToken(token) { + return errorsmod.Wrap(ibcerrors.ErrInvalidCoins, token.String()) + } + if err := ValidateIBCDenom(token.GetDenom()); err != nil { + return errorsmod.Wrap(ibcerrors.ErrInvalidCoins, token.Denom) + } + } + + return nil +} + +// GetTokens returns the tokens which will be transferred. +func (msg MsgTransfer) GetTokens() []sdk.Coin { + tokens := msg.Tokens + if isValidToken(msg.Token) { + tokens = []sdk.Coin{msg.Token} + } + return tokens +} + +// isValidToken returns true if the token provided is valid, +// and should be used to transfer tokens. +// this function is used in case the user constructs a sdk.Coin literal +// instead of using the construction function. +func isValidToken(coin sdk.Coin) bool { + if coin.IsNil() { + return false + } + + if strings.TrimSpace(coin.Denom) == "" { + return false + } + + if coin.Amount.IsZero() { + return false + } + + if coin.Amount.IsNegative() { + return false + } + + return true } diff --git a/modules/apps/transfer/types/msgs_test.go b/modules/apps/transfer/types/msgs_test.go index e9bb11cac54..e98400bfa45 100644 --- a/modules/apps/transfer/types/msgs_test.go +++ b/modules/apps/transfer/types/msgs_test.go @@ -38,11 +38,11 @@ var ( receiver = sdk.AccAddress("testaddr2").String() emptyAddr string - coin = sdk.NewCoin("atom", sdkmath.NewInt(100)) - ibcCoin = sdk.NewCoin("ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdkmath.NewInt(100)) - invalidIBCCoin = sdk.NewCoin("ibc/7F1D3FCF4AE79E1554", sdkmath.NewInt(100)) - invalidDenomCoin = sdk.Coin{Denom: "0atom", Amount: sdkmath.NewInt(100)} - zeroCoin = sdk.Coin{Denom: "atoms", Amount: sdkmath.NewInt(0)} + coins = sdk.NewCoins(sdk.NewCoin("atom", sdkmath.NewInt(100))) + ibcCoins = sdk.NewCoins(sdk.NewCoin("ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdkmath.NewInt(100))) + invalidIBCCoins = sdk.NewCoins(sdk.NewCoin("ibc/7F1D3FCF4AE79E1554", sdkmath.NewInt(100))) + invalidDenomCoins = []sdk.Coin{{Denom: "0atom", Amount: sdkmath.NewInt(100)}} + zeroCoins = sdk.NewCoins(sdk.Coin{Denom: "atoms", Amount: sdkmath.NewInt(0)}) timeoutHeight = clienttypes.NewHeight(0, 10) ) @@ -54,22 +54,26 @@ func TestMsgTransferValidation(t *testing.T) { msg *types.MsgTransfer expPass bool }{ - {"valid msg with base denom", types.NewMsgTransfer(validPort, validChannel, coin, sender, receiver, timeoutHeight, 0, ""), true}, - {"valid msg with trace hash", types.NewMsgTransfer(validPort, validChannel, ibcCoin, sender, receiver, timeoutHeight, 0, ""), true}, - {"invalid ibc denom", types.NewMsgTransfer(validPort, validChannel, invalidIBCCoin, sender, receiver, timeoutHeight, 0, ""), false}, - {"too short port id", types.NewMsgTransfer(invalidShortPort, validChannel, coin, sender, receiver, timeoutHeight, 0, ""), false}, - {"too long port id", types.NewMsgTransfer(invalidLongPort, validChannel, coin, sender, receiver, timeoutHeight, 0, ""), false}, - {"port id contains non-alpha", types.NewMsgTransfer(invalidPort, validChannel, coin, sender, receiver, timeoutHeight, 0, ""), false}, - {"too short channel id", types.NewMsgTransfer(validPort, invalidShortChannel, coin, sender, receiver, timeoutHeight, 0, ""), false}, - {"too long channel id", types.NewMsgTransfer(validPort, invalidLongChannel, coin, sender, receiver, timeoutHeight, 0, ""), false}, - {"too long memo", types.NewMsgTransfer(validPort, validChannel, coin, sender, receiver, timeoutHeight, 0, ibctesting.GenerateString(types.MaximumMemoLength+1)), false}, - {"channel id contains non-alpha", types.NewMsgTransfer(validPort, invalidChannel, coin, sender, receiver, timeoutHeight, 0, ""), false}, - {"invalid denom", types.NewMsgTransfer(validPort, validChannel, invalidDenomCoin, sender, receiver, timeoutHeight, 0, ""), false}, - {"zero coin", types.NewMsgTransfer(validPort, validChannel, zeroCoin, sender, receiver, timeoutHeight, 0, ""), false}, - {"missing sender address", types.NewMsgTransfer(validPort, validChannel, coin, emptyAddr, receiver, timeoutHeight, 0, ""), false}, - {"missing recipient address", types.NewMsgTransfer(validPort, validChannel, coin, sender, "", timeoutHeight, 0, ""), false}, - {"too long recipient address", types.NewMsgTransfer(validPort, validChannel, coin, sender, ibctesting.GenerateString(types.MaximumReceiverLength+1), timeoutHeight, 0, ""), false}, - {"empty coin", types.NewMsgTransfer(validPort, validChannel, sdk.Coin{}, sender, receiver, timeoutHeight, 0, ""), false}, + {"valid msg with base denom", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ""), true}, + {"valid msg with trace hash", types.NewMsgTransfer(validPort, validChannel, ibcCoins, sender, receiver, timeoutHeight, 0, ""), true}, + {"multidenom", types.NewMsgTransfer(validPort, validChannel, coins.Add(ibcCoins...), sender, receiver, timeoutHeight, 0, ""), true}, + {"invalid ibc denom", types.NewMsgTransfer(validPort, validChannel, invalidIBCCoins, sender, receiver, timeoutHeight, 0, ""), false}, + {"too short port id", types.NewMsgTransfer(invalidShortPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, + {"too long port id", types.NewMsgTransfer(invalidLongPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, + {"port id contains non-alpha", types.NewMsgTransfer(invalidPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, + {"too short channel id", types.NewMsgTransfer(validPort, invalidShortChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, + {"too long channel id", types.NewMsgTransfer(validPort, invalidLongChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, + {"too long memo", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ibctesting.GenerateString(types.MaximumMemoLength+1)), false}, + {"channel id contains non-alpha", types.NewMsgTransfer(validPort, invalidChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, + {"invalid denom", types.NewMsgTransfer(validPort, validChannel, invalidDenomCoins, sender, receiver, timeoutHeight, 0, ""), false}, + {"zero coins", types.NewMsgTransfer(validPort, validChannel, zeroCoins, sender, receiver, timeoutHeight, 0, ""), false}, + {"missing sender address", types.NewMsgTransfer(validPort, validChannel, coins, emptyAddr, receiver, timeoutHeight, 0, ""), false}, + {"missing recipient address", types.NewMsgTransfer(validPort, validChannel, coins, sender, "", timeoutHeight, 0, ""), false}, + {"too long recipient address", types.NewMsgTransfer(validPort, validChannel, coins, sender, ibctesting.GenerateString(types.MaximumReceiverLength+1), timeoutHeight, 0, ""), false}, + {"empty coins", types.NewMsgTransfer(validPort, validChannel, sdk.NewCoins(), sender, receiver, timeoutHeight, 0, ""), false}, + {"multidenom: invalid denom", types.NewMsgTransfer(validPort, validChannel, coins.Add(invalidDenomCoins...), sender, receiver, timeoutHeight, 0, ""), false}, + {"multidenom: invalid ibc denom", types.NewMsgTransfer(validPort, validChannel, coins.Add(invalidIBCCoins...), sender, receiver, timeoutHeight, 0, ""), false}, + {"multidenom: zero coins", types.NewMsgTransfer(validPort, validChannel, zeroCoins, sender, receiver, timeoutHeight, 0, ""), false}, } for i, tc := range testCases { @@ -87,7 +91,7 @@ func TestMsgTransferValidation(t *testing.T) { // TestMsgTransferGetSigners tests GetSigners for MsgTransfer func TestMsgTransferGetSigners(t *testing.T) { addr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - msg := types.NewMsgTransfer(validPort, validChannel, coin, addr.String(), receiver, timeoutHeight, 0, "") + msg := types.NewMsgTransfer(validPort, validChannel, coins, addr.String(), receiver, timeoutHeight, 0, "") encodingCfg := moduletestutil.MakeTestEncodingConfig(transfer.AppModuleBasic{}) signers, _, err := encodingCfg.Codec.GetMsgV1Signers(msg) diff --git a/modules/apps/transfer/types/transfer_authorization.go b/modules/apps/transfer/types/transfer_authorization.go index 242bc86e801..b38702f980a 100644 --- a/modules/apps/transfer/types/transfer_authorization.go +++ b/modules/apps/transfer/types/transfer_authorization.go @@ -44,6 +44,9 @@ func (a TransferAuthorization) Accept(ctx context.Context, msg proto.Message) (a return authz.AcceptResponse{}, errorsmod.Wrap(ibcerrors.ErrInvalidType, "type mismatch") } + // TODO: replace with correct usage in https://github.com/cosmos/ibc-go/issues/5802 + token := msgTransfer.GetTokens()[0] + for index, allocation := range a.Allocations { if !(allocation.SourceChannel == msgTransfer.SourceChannel && allocation.SourcePort == msgTransfer.SourcePort) { continue @@ -59,11 +62,11 @@ func (a TransferAuthorization) Accept(ctx context.Context, msg proto.Message) (a } // If the spend limit is set to the MaxUint256 sentinel value, do not subtract the amount from the spend limit. - if allocation.SpendLimit.AmountOf(msgTransfer.Token.Denom).Equal(UnboundedSpendLimit()) { + if allocation.SpendLimit.AmountOf(token.Denom).Equal(UnboundedSpendLimit()) { return authz.AcceptResponse{Accept: true, Delete: false, Updated: nil}, nil } - limitLeft, isNegative := allocation.SpendLimit.SafeSub(msgTransfer.Token) + limitLeft, isNegative := allocation.SpendLimit.SafeSub(token) if isNegative { return authz.AcceptResponse{}, errorsmod.Wrapf(ibcerrors.ErrInsufficientFunds, "requested amount is more than spend limit") } diff --git a/modules/apps/transfer/types/transfer_authorization_test.go b/modules/apps/transfer/types/transfer_authorization_test.go index 781e5d68e3d..d86c396d335 100644 --- a/modules/apps/transfer/types/transfer_authorization_test.go +++ b/modules/apps/transfer/types/transfer_authorization_test.go @@ -15,7 +15,7 @@ const testMemo = `{"wasm":{"contract":"osmo1c3ljch9dfw5kf52nfwpxd2zmj2ese7agnx0p func (suite *TypesTestSuite) TestTransferAuthorizationAccept() { var ( - msgTransfer types.MsgTransfer + msgTransfer *types.MsgTransfer transferAuthz types.TransferAuthorization ) @@ -247,18 +247,20 @@ func (suite *TypesTestSuite) TestTransferAuthorizationAccept() { }, } - msgTransfer = types.MsgTransfer{ - SourcePort: path.EndpointA.ChannelConfig.PortID, - SourceChannel: path.EndpointA.ChannelID, - Token: ibctesting.TestCoin, - Sender: suite.chainA.SenderAccount.GetAddress().String(), - Receiver: ibctesting.TestAccAddress, - TimeoutHeight: suite.chainB.GetTimeoutHeight(), - } + msgTransfer = types.NewMsgTransfer( + path.EndpointA.ChannelConfig.PortID, + path.EndpointA.ChannelID, + sdk.NewCoins(ibctesting.TestCoin), + suite.chainA.SenderAccount.GetAddress().String(), + ibctesting.TestAccAddress, + suite.chainB.GetTimeoutHeight(), + 0, + "", + ) tc.malleate() - res, err := transferAuthz.Accept(suite.chainA.GetContext(), &msgTransfer) + res, err := transferAuthz.Accept(suite.chainA.GetContext(), msgTransfer) tc.assertResult(res, err) }) } diff --git a/modules/apps/transfer/types/tx.pb.go b/modules/apps/transfer/types/tx.pb.go index 6dbba5651c9..860b32b2549 100644 --- a/modules/apps/transfer/types/tx.pb.go +++ b/modules/apps/transfer/types/tx.pb.go @@ -54,6 +54,8 @@ type MsgTransfer struct { TimeoutTimestamp uint64 `protobuf:"varint,7,opt,name=timeout_timestamp,json=timeoutTimestamp,proto3" json:"timeout_timestamp,omitempty"` // optional memo Memo string `protobuf:"bytes,8,opt,name=memo,proto3" json:"memo,omitempty"` + // tokens to be transferred + Tokens []types.Coin `protobuf:"bytes,9,rep,name=tokens,proto3" json:"tokens"` } func (m *MsgTransfer) Reset() { *m = MsgTransfer{} } @@ -221,46 +223,47 @@ func init() { } var fileDescriptor_7401ed9bed2f8e09 = []byte{ - // 613 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0xcf, 0x4f, 0x13, 0x4f, - 0x14, 0xef, 0x7e, 0x29, 0xfd, 0xc2, 0x54, 0x40, 0x56, 0x03, 0xcb, 0xc6, 0x6c, 0x49, 0x23, 0x09, - 0x96, 0x30, 0x93, 0x62, 0x0c, 0xa6, 0xc7, 0x72, 0xf1, 0x20, 0x09, 0x36, 0x78, 0xf1, 0x42, 0x76, - 0xa7, 0xcf, 0xed, 0x84, 0xee, 0xcc, 0x3a, 0x33, 0x6d, 0xf4, 0x62, 0x88, 0x27, 0xe3, 0xc9, 0x3f, - 0xc1, 0xa3, 0x47, 0xfe, 0x0c, 0x8e, 0x1c, 0x3d, 0x19, 0x03, 0x07, 0x2e, 0xfe, 0x11, 0x66, 0x66, - 0xa7, 0x75, 0xf5, 0x50, 0xf5, 0xb2, 0xfb, 0x7e, 0x7c, 0xde, 0xaf, 0xcf, 0x9b, 0x87, 0xb6, 0x58, - 0x42, 0x49, 0x9c, 0xe7, 0x43, 0x46, 0x63, 0xcd, 0x04, 0x57, 0x44, 0xcb, 0x98, 0xab, 0x97, 0x20, - 0xc9, 0xb8, 0x4d, 0xf4, 0x6b, 0x9c, 0x4b, 0xa1, 0x85, 0x7f, 0x8f, 0x25, 0x14, 0x97, 0x61, 0x78, - 0x02, 0xc3, 0xe3, 0x76, 0xb8, 0x1a, 0x67, 0x8c, 0x0b, 0x62, 0xbf, 0x45, 0x40, 0x78, 0x37, 0x15, - 0xa9, 0xb0, 0x22, 0x31, 0x92, 0xb3, 0xae, 0x53, 0xa1, 0x32, 0xa1, 0x48, 0xa6, 0x52, 0x93, 0x3e, - 0x53, 0xa9, 0x73, 0x44, 0xce, 0x91, 0xc4, 0x0a, 0xc8, 0xb8, 0x9d, 0x80, 0x8e, 0xdb, 0x84, 0x0a, - 0xc6, 0x9d, 0xbf, 0x61, 0xda, 0xa4, 0x42, 0x02, 0xa1, 0x43, 0x06, 0x5c, 0x9b, 0xe8, 0x42, 0x72, - 0x80, 0x9d, 0xd9, 0x73, 0x4c, 0x9a, 0xb5, 0xe0, 0xe6, 0xd9, 0x1c, 0xaa, 0x1f, 0xaa, 0xf4, 0xd8, - 0x59, 0xfd, 0x06, 0xaa, 0x2b, 0x31, 0x92, 0x14, 0x4e, 0x72, 0x21, 0x75, 0xe0, 0x6d, 0x7a, 0xdb, - 0x8b, 0x3d, 0x54, 0x98, 0x8e, 0x84, 0xd4, 0xfe, 0x16, 0x5a, 0x76, 0x00, 0x3a, 0x88, 0x39, 0x87, - 0x61, 0xf0, 0x9f, 0xc5, 0x2c, 0x15, 0xd6, 0x83, 0xc2, 0xe8, 0x77, 0xd0, 0xbc, 0x16, 0xa7, 0xc0, - 0x83, 0xb9, 0x4d, 0x6f, 0xbb, 0xbe, 0xb7, 0x81, 0x8b, 0xa9, 0xb0, 0x99, 0x0a, 0xbb, 0xa9, 0xf0, - 0x81, 0x60, 0xbc, 0xbb, 0x78, 0xf1, 0xb5, 0x51, 0xf9, 0x7c, 0x73, 0xde, 0xf2, 0x7a, 0x45, 0x88, - 0xbf, 0x86, 0x6a, 0x0a, 0x78, 0x1f, 0x64, 0x50, 0xb5, 0xa9, 0x9d, 0xe6, 0x87, 0x68, 0x41, 0x02, - 0x05, 0x36, 0x06, 0x19, 0xcc, 0x5b, 0xcf, 0x54, 0xf7, 0x9f, 0xa2, 0x65, 0xcd, 0x32, 0x10, 0x23, - 0x7d, 0x32, 0x00, 0x96, 0x0e, 0x74, 0x50, 0xb3, 0x85, 0x43, 0x6c, 0xd6, 0x65, 0xe8, 0xc2, 0x8e, - 0xa4, 0x71, 0x1b, 0x3f, 0xb1, 0x88, 0x72, 0xe5, 0x25, 0x17, 0x5c, 0x78, 0xfc, 0x1d, 0xb4, 0x3a, - 0xc9, 0x66, 0xfe, 0x4a, 0xc7, 0x59, 0x1e, 0xfc, 0xbf, 0xe9, 0x6d, 0x57, 0x7b, 0xb7, 0x9d, 0xe3, - 0x78, 0x62, 0xf7, 0x7d, 0x54, 0xcd, 0x20, 0x13, 0xc1, 0x82, 0x6d, 0xc9, 0xca, 0x9d, 0xd6, 0xfb, - 0x4f, 0x8d, 0xca, 0xbb, 0x9b, 0xf3, 0x96, 0xeb, 0xfd, 0xc3, 0xcd, 0x79, 0x6b, 0xad, 0xa0, 0x60, - 0x57, 0xf5, 0x4f, 0x49, 0x89, 0xf2, 0xe6, 0x3e, 0xba, 0x53, 0x52, 0x7b, 0xa0, 0x72, 0xc1, 0x15, - 0x98, 0x69, 0x15, 0xbc, 0x1a, 0x01, 0xa7, 0x60, 0xd7, 0x50, 0xed, 0x4d, 0xf5, 0x4e, 0xd5, 0xa4, - 0x6f, 0xbe, 0x45, 0x2b, 0x87, 0x2a, 0x7d, 0x9e, 0xf7, 0x63, 0x0d, 0x47, 0xb1, 0x8c, 0x33, 0x65, - 0xa9, 0x63, 0x29, 0x07, 0xe9, 0x36, 0xe7, 0x34, 0xbf, 0x8b, 0x6a, 0xb9, 0x45, 0xd8, 0x6d, 0xd5, - 0xf7, 0xee, 0xe3, 0x59, 0xaf, 0x18, 0x17, 0xd9, 0xba, 0x55, 0x43, 0x50, 0xcf, 0x45, 0x76, 0x56, - 0x7e, 0xce, 0x64, 0x93, 0x36, 0x37, 0xd0, 0xfa, 0x6f, 0xf5, 0x27, 0xcd, 0xef, 0x7d, 0xf7, 0xd0, - 0xdc, 0xa1, 0x4a, 0xfd, 0x01, 0x5a, 0x98, 0x3e, 0xad, 0x07, 0xb3, 0x6b, 0x96, 0x38, 0x08, 0xdb, - 0x7f, 0x0d, 0x9d, 0xd2, 0xa5, 0xd1, 0xad, 0x5f, 0x98, 0xd8, 0xfd, 0x63, 0x8a, 0x32, 0x3c, 0x7c, - 0xf4, 0x4f, 0xf0, 0x49, 0xd5, 0x70, 0xfe, 0xcc, 0x3c, 0x9f, 0xee, 0xb3, 0x8b, 0xab, 0xc8, 0xbb, - 0xbc, 0x8a, 0xbc, 0x6f, 0x57, 0x91, 0xf7, 0xf1, 0x3a, 0xaa, 0x5c, 0x5e, 0x47, 0x95, 0x2f, 0xd7, - 0x51, 0xe5, 0xc5, 0x7e, 0xca, 0xf4, 0x60, 0x94, 0x60, 0x2a, 0x32, 0xe2, 0x0e, 0x9b, 0x25, 0x74, - 0x37, 0x15, 0x64, 0xfc, 0x98, 0x64, 0xa2, 0x3f, 0x1a, 0x82, 0x32, 0xc7, 0x5a, 0x3a, 0x52, 0xfd, - 0x26, 0x07, 0x95, 0xd4, 0xec, 0x7d, 0x3e, 0xfc, 0x11, 0x00, 0x00, 0xff, 0xff, 0x3b, 0xda, 0xd1, - 0xc3, 0x96, 0x04, 0x00, 0x00, + // 630 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xb1, 0x6e, 0x13, 0x4d, + 0x10, 0xf6, 0xfd, 0x76, 0xfc, 0x27, 0x6b, 0x92, 0x90, 0x05, 0x25, 0x17, 0x0b, 0x9d, 0x2d, 0x8b, + 0x48, 0xc1, 0x51, 0x76, 0xe5, 0x20, 0x14, 0x64, 0x51, 0x39, 0x0d, 0x05, 0x91, 0x82, 0x15, 0x1a, + 0x9a, 0xe8, 0x6e, 0x3d, 0x9c, 0x57, 0xf1, 0xed, 0x1e, 0xb7, 0x6b, 0x0b, 0x1a, 0x84, 0xa8, 0x10, + 0x15, 0x8f, 0x40, 0x49, 0x99, 0x27, 0xa0, 0x4e, 0x99, 0x92, 0x0a, 0xa1, 0xa4, 0x48, 0xc3, 0x43, + 0xa0, 0xdd, 0x5b, 0x9b, 0x83, 0x22, 0x84, 0xc6, 0xde, 0x99, 0xf9, 0xe6, 0x9b, 0xf9, 0x66, 0x3c, + 0x46, 0x1b, 0x3c, 0x62, 0x34, 0x4c, 0xd3, 0x11, 0x67, 0xa1, 0xe6, 0x52, 0x28, 0xaa, 0xb3, 0x50, + 0xa8, 0x17, 0x90, 0xd1, 0x49, 0x87, 0xea, 0x57, 0x24, 0xcd, 0xa4, 0x96, 0xf8, 0x0e, 0x8f, 0x18, + 0x29, 0xc2, 0xc8, 0x14, 0x46, 0x26, 0x9d, 0xfa, 0x4a, 0x98, 0x70, 0x21, 0xa9, 0xfd, 0xcc, 0x13, + 0xea, 0xb7, 0x63, 0x19, 0x4b, 0xfb, 0xa4, 0xe6, 0xe5, 0xbc, 0x6b, 0x4c, 0xaa, 0x44, 0x2a, 0x9a, + 0xa8, 0xd8, 0xd0, 0x27, 0x2a, 0x76, 0x81, 0xc0, 0x05, 0xa2, 0x50, 0x01, 0x9d, 0x74, 0x22, 0xd0, + 0x61, 0x87, 0x32, 0xc9, 0x85, 0x8b, 0x37, 0x4c, 0x9b, 0x4c, 0x66, 0x40, 0xd9, 0x88, 0x83, 0xd0, + 0x26, 0x3b, 0x7f, 0x39, 0xc0, 0xd6, 0xd5, 0x3a, 0xa6, 0xcd, 0x5a, 0x70, 0xeb, 0x4b, 0x19, 0xd5, + 0xf6, 0x55, 0x7c, 0xe8, 0xbc, 0xb8, 0x81, 0x6a, 0x4a, 0x8e, 0x33, 0x06, 0x47, 0xa9, 0xcc, 0xb4, + 0xef, 0x35, 0xbd, 0xcd, 0x85, 0x3e, 0xca, 0x5d, 0x07, 0x32, 0xd3, 0x78, 0x03, 0x2d, 0x39, 0x00, + 0x1b, 0x86, 0x42, 0xc0, 0xc8, 0xff, 0xcf, 0x62, 0x16, 0x73, 0xef, 0x5e, 0xee, 0xc4, 0x5d, 0x34, + 0xa7, 0xe5, 0x31, 0x08, 0xbf, 0xdc, 0xf4, 0x36, 0x6b, 0x3b, 0xeb, 0x24, 0x57, 0x45, 0x8c, 0x2a, + 0xe2, 0x54, 0x91, 0x3d, 0xc9, 0x45, 0x6f, 0xe1, 0xf4, 0x5b, 0xa3, 0xf4, 0xf9, 0xf2, 0xa4, 0xed, + 0xf5, 0xf3, 0x14, 0xbc, 0x8a, 0xaa, 0x0a, 0xc4, 0x00, 0x32, 0xbf, 0x62, 0xa9, 0x9d, 0x85, 0xeb, + 0x68, 0x3e, 0x03, 0x06, 0x7c, 0x02, 0x99, 0x3f, 0x67, 0x23, 0x33, 0x1b, 0x3f, 0x41, 0x4b, 0x9a, + 0x27, 0x20, 0xc7, 0xfa, 0x68, 0x08, 0x3c, 0x1e, 0x6a, 0xbf, 0x6a, 0x0b, 0xd7, 0x89, 0x59, 0x97, + 0x19, 0x17, 0x71, 0x43, 0x9a, 0x74, 0xc8, 0x63, 0x8b, 0x28, 0x56, 0x5e, 0x74, 0xc9, 0x79, 0x04, + 0x6f, 0xa1, 0x95, 0x29, 0x9b, 0xf9, 0x56, 0x3a, 0x4c, 0x52, 0xff, 0xff, 0xa6, 0xb7, 0x59, 0xe9, + 0xdf, 0x74, 0x81, 0xc3, 0xa9, 0x1f, 0x63, 0x54, 0x49, 0x20, 0x91, 0xfe, 0xbc, 0x6d, 0xc9, 0xbe, + 0xf1, 0x23, 0x54, 0xb5, 0x5a, 0x94, 0xbf, 0xd0, 0x2c, 0x5f, 0x5b, 0xbf, 0xcb, 0xe9, 0xb6, 0xdf, + 0x7f, 0x6a, 0x94, 0xde, 0x5d, 0x9e, 0xb4, 0x9d, 0xf2, 0x0f, 0x97, 0x27, 0xed, 0xd5, 0x9c, 0x60, + 0x5b, 0x0d, 0x8e, 0x69, 0x61, 0x61, 0xad, 0x5d, 0x74, 0xab, 0x60, 0xf6, 0x41, 0xa5, 0x52, 0x28, + 0x30, 0xb3, 0x52, 0xf0, 0x72, 0x0c, 0x82, 0x81, 0x5d, 0x62, 0xa5, 0x3f, 0xb3, 0xbb, 0x15, 0x43, + 0xdf, 0x7a, 0x83, 0x96, 0xf7, 0x55, 0xfc, 0x2c, 0x1d, 0x84, 0x1a, 0x0e, 0xc2, 0x2c, 0x4c, 0x94, + 0x1d, 0x3c, 0x8f, 0x05, 0x64, 0x6e, 0xef, 0xce, 0xc2, 0x3d, 0x54, 0x4d, 0x2d, 0xc2, 0xee, 0xba, + 0xb6, 0x73, 0x97, 0x5c, 0x75, 0x03, 0x24, 0x67, 0xeb, 0x55, 0x8c, 0xb0, 0xbe, 0xcb, 0xec, 0x2e, + 0xff, 0xd2, 0x64, 0x49, 0x5b, 0xeb, 0x68, 0xed, 0x8f, 0xfa, 0xd3, 0xe6, 0x77, 0x7e, 0x78, 0xa8, + 0xbc, 0xaf, 0x62, 0x3c, 0x44, 0xf3, 0xb3, 0x1f, 0xe6, 0xbd, 0xab, 0x6b, 0x16, 0x66, 0x50, 0xef, + 0x5c, 0x1b, 0x3a, 0x1b, 0x97, 0x46, 0x37, 0x7e, 0x9b, 0xc4, 0xf6, 0x5f, 0x29, 0x8a, 0xf0, 0xfa, + 0x83, 0x7f, 0x82, 0x4f, 0xab, 0xd6, 0xe7, 0xde, 0x9a, 0xb5, 0xf7, 0x9e, 0x9e, 0x9e, 0x07, 0xde, + 0xd9, 0x79, 0xe0, 0x7d, 0x3f, 0x0f, 0xbc, 0x8f, 0x17, 0x41, 0xe9, 0xec, 0x22, 0x28, 0x7d, 0xbd, + 0x08, 0x4a, 0xcf, 0x77, 0x63, 0xae, 0x87, 0xe3, 0x88, 0x30, 0x99, 0x50, 0xf7, 0xb7, 0xc0, 0x23, + 0xb6, 0x1d, 0x4b, 0x3a, 0x79, 0x48, 0x13, 0x39, 0x18, 0x8f, 0x40, 0x99, 0x53, 0x2f, 0x9c, 0xb8, + 0x7e, 0x9d, 0x82, 0x8a, 0xaa, 0xf6, 0xba, 0xef, 0xff, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x29, 0xee, + 0x20, 0x91, 0xd4, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -403,6 +406,20 @@ func (m *MsgTransfer) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Tokens) > 0 { + for iNdEx := len(m.Tokens) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Tokens[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + } if len(m.Memo) > 0 { i -= len(m.Memo) copy(dAtA[i:], m.Memo) @@ -601,6 +618,12 @@ func (m *MsgTransfer) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } + if len(m.Tokens) > 0 { + for _, e := range m.Tokens { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } return n } @@ -920,6 +943,40 @@ func (m *MsgTransfer) Unmarshal(dAtA []byte) error { } m.Memo = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tokens", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Tokens = append(m.Tokens, types.Coin{}) + if err := m.Tokens[len(m.Tokens)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) diff --git a/proto/ibc/applications/transfer/v1/tx.proto b/proto/ibc/applications/transfer/v1/tx.proto index 42c70d3bedc..52e5d29b7e1 100644 --- a/proto/ibc/applications/transfer/v1/tx.proto +++ b/proto/ibc/applications/transfer/v1/tx.proto @@ -49,6 +49,8 @@ message MsgTransfer { uint64 timeout_timestamp = 7; // optional memo string memo = 8; + // tokens to be transferred + repeated cosmos.base.v1beta1.Coin tokens = 9 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true]; } // MsgTransferResponse defines the Msg/Transfer response type. From 4cc6a853f9696ad2a908b1e0f406794c53567998 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 15 Apr 2024 18:18:16 +0200 Subject: [PATCH 04/42] fix: allow base denom with trailing slash (#6148) --- modules/apps/transfer/types/trace.go | 4 ++-- modules/apps/transfer/types/trace_test.go | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/apps/transfer/types/trace.go b/modules/apps/transfer/types/trace.go index 93aa7ee14bf..ee7b3c2ad50 100644 --- a/modules/apps/transfer/types/trace.go +++ b/modules/apps/transfer/types/trace.go @@ -199,11 +199,11 @@ func ValidatePrefixedDenom(denom string) error { return nil } - if strings.TrimSpace(denomSplit[len(denomSplit)-1]) == "" { + path, baseDenom := extractPathAndBaseFromFullDenom(denomSplit) + if strings.TrimSpace(baseDenom) == "" { return errorsmod.Wrap(ErrInvalidDenomForTransfer, "base denomination cannot be blank") } - path, _ := extractPathAndBaseFromFullDenom(denomSplit) if path == "" { // NOTE: base denom contains slashes, so no base denomination validation return nil diff --git a/modules/apps/transfer/types/trace_test.go b/modules/apps/transfer/types/trace_test.go index 49623ed1dec..f7634a7a3b0 100644 --- a/modules/apps/transfer/types/trace_test.go +++ b/modules/apps/transfer/types/trace_test.go @@ -132,15 +132,16 @@ func TestValidatePrefixedDenom(t *testing.T) { expError bool }{ {"prefixed denom", "transfer/channel-1/uatom", false}, + {"prefixed denom with base denom with leading slash", "transfer/channel-1/uatom/", false}, {"prefixed denom with '/'", "transfer/channel-1/gamm/pool/1", false}, {"empty prefix", "/uatom", false}, {"empty identifiers", "//uatom", false}, {"base denom", "uatom", false}, {"base denom with single '/'", "erc20/0x85bcBCd7e79Ec36f4fBBDc54F90C643d921151AA", false}, {"base denom with multiple '/'s", "gamm/pool/1", false}, + {"single trace identifier", "transfer/", false}, {"invalid port ID", "(transfer)/channel-1/uatom", true}, {"empty denom", "", true}, - {"single trace identifier", "transfer/", true}, } for _, tc := range testCases { From 71f830cb0f1f01ab1ee44c1d977f4731c31934b7 Mon Sep 17 00:00:00 2001 From: Charly Date: Tue, 16 Apr 2024 14:13:19 +0200 Subject: [PATCH 05/42] imp: add CurrentVersion, EscrowVersion (#6160) * add CurrentVersion, EscrowVersion, update tests * update hardcoded transfer channel version from interchaintest --- e2e/testsuite/testsuite.go | 4 ++++ modules/apps/transfer/keeper/mbt_relay_test.go | 2 +- modules/apps/transfer/types/keys.go | 18 +++++++++++++----- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/e2e/testsuite/testsuite.go b/e2e/testsuite/testsuite.go index 48dbc13adcc..dc188180aa1 100644 --- a/e2e/testsuite/testsuite.go +++ b/e2e/testsuite/testsuite.go @@ -109,6 +109,10 @@ func (s *E2ETestSuite) ConfigureRelayer(ctx context.Context, chainA, chainB ibc. pathName := s.generatePathName() channelOptions := ibc.DefaultChannelOpts() + // TODO: better way to do this. + // For now, set the version to the latest transfer module version + channelOptions.Version = transfertypes.Version + if channelOpts != nil { channelOpts(&channelOptions) } diff --git a/modules/apps/transfer/keeper/mbt_relay_test.go b/modules/apps/transfer/keeper/mbt_relay_test.go index 72c5f0b1f41..e2abbde9f1a 100644 --- a/modules/apps/transfer/keeper/mbt_relay_test.go +++ b/modules/apps/transfer/keeper/mbt_relay_test.go @@ -108,7 +108,7 @@ func AddressFromTla(addr []string) string { s = addr[2] } else if len(addr[2]) == 0 { // escrow address: ics20-1\x00port/channel - s = fmt.Sprintf("%s\x00%s/%s", types.Version, addr[0], addr[1]) + s = fmt.Sprintf("%s\x00%s/%s", types.Version1, addr[0], addr[1]) } else { panic(errors.New("failed to convert from TLA+ address: neither simple nor escrow address")) } diff --git a/modules/apps/transfer/types/keys.go b/modules/apps/transfer/types/keys.go index 0b13912cc88..8db04bb87bb 100644 --- a/modules/apps/transfer/types/keys.go +++ b/modules/apps/transfer/types/keys.go @@ -11,10 +11,6 @@ const ( // ModuleName defines the IBC transfer name ModuleName = "transfer" - // Version defines the current version the IBC transfer - // module supports - Version = "ics20-1" - // PortID is the default port id that transfer module binds to PortID = "transfer" @@ -38,6 +34,18 @@ const ( ParamsKey = "params" ) +const ( + // Version defines the current version the IBC transfer + // module supports + Version = "ics20-2" + + // Version1 defines first version of the IBC transfer module + Version1 = "ics20-1" + + // escrowAddressVersion should remain as ics20-1 to avoid the address changing. + escrowAddressVersion = "ics20-1" +) + var ( // PortKey defines the key to store the port ID in store PortKey = []byte{0x01} @@ -54,7 +62,7 @@ func GetEscrowAddress(portID, channelID string) sdk.AccAddress { contents := fmt.Sprintf("%s/%s", portID, channelID) // ADR 028 AddressHash construction - preImage := []byte(Version) + preImage := []byte(escrowAddressVersion) preImage = append(preImage, 0) preImage = append(preImage, contents...) hash := sha256.Sum256(preImage) From 28ff9b64cbd31401d70427a7c72a1aff730c07f7 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Tue, 16 Apr 2024 14:00:29 +0100 Subject: [PATCH 06/42] chore: add function for converting packet data from v1 to v3 (#6116) --------- Co-authored-by: Charly --- .../apps/transfer/internal/convert/convert.go | 55 ++++++++ .../transfer/internal/convert/convert_test.go | 123 ++++++++++++++++++ modules/apps/transfer/internal/denom/denom.go | 39 ++++++ modules/apps/transfer/types/trace.go | 42 +----- modules/apps/transfer/types/v3/packet.go | 15 +++ 5 files changed, 237 insertions(+), 37 deletions(-) create mode 100644 modules/apps/transfer/internal/convert/convert.go create mode 100644 modules/apps/transfer/internal/convert/convert_test.go create mode 100644 modules/apps/transfer/internal/denom/denom.go create mode 100644 modules/apps/transfer/types/v3/packet.go diff --git a/modules/apps/transfer/internal/convert/convert.go b/modules/apps/transfer/internal/convert/convert.go new file mode 100644 index 00000000000..34ba1da17e6 --- /dev/null +++ b/modules/apps/transfer/internal/convert/convert.go @@ -0,0 +1,55 @@ +package convert + +import ( + "strings" + + v1types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" +) + +// PacketDataV1ToV3 converts a v1 (ICS20-V1) packet data to a v3 (ICS20-V2) packet data. +func PacketDataV1ToV3(packetData v1types.FungibleTokenPacketData) v3types.FungibleTokenPacketData { + if err := packetData.ValidateBasic(); err != nil { + panic(err) + } + + v2Denom, trace := extractDenomAndTraceFromV1Denom(packetData.Denom) + return v3types.FungibleTokenPacketData{ + Tokens: []*v3types.Token{ + { + Denom: v2Denom, + Amount: packetData.Amount, + Trace: trace, + }, + }, + Sender: packetData.Sender, + Receiver: packetData.Receiver, + Memo: packetData.Memo, + } +} + +// extractDenomAndTraceFromV1Denom extracts the base denom and remaining trace from a v1 IBC denom. +func extractDenomAndTraceFromV1Denom(v1Denom string) (string, []string) { + v1DenomTrace := v1types.ParseDenomTrace(v1Denom) + + splitPath := strings.Split(v1DenomTrace.Path, "/") + + // if the path slice is empty, then the base denom is the full native denom. + if len(splitPath) == 0 { + return v1DenomTrace.BaseDenom, nil + } + + // this condition should never be reached. + if len(splitPath)%2 != 0 { + panic("pathSlice length is not even") + } + + // the path slices consists of entries of ports and channel ids separately, + // we need to combine them to form the trace. + var trace []string + for i := 0; i < len(splitPath); i += 2 { + trace = append(trace, strings.Join(splitPath[i:i+2], "/")) + } + + return v1DenomTrace.BaseDenom, trace +} diff --git a/modules/apps/transfer/internal/convert/convert_test.go b/modules/apps/transfer/internal/convert/convert_test.go new file mode 100644 index 00000000000..f43731ad5dd --- /dev/null +++ b/modules/apps/transfer/internal/convert/convert_test.go @@ -0,0 +1,123 @@ +package convert + +import ( + "testing" + + "github.com/stretchr/testify/require" + + errorsmod "cosmossdk.io/errors" + + v1types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" +) + +func TestConvertPacketV1ToPacketV3(t *testing.T) { + const ( + sender = "sender" + receiver = "receiver" + ) + + testCases := []struct { + name string + v1Data v1types.FungibleTokenPacketData + v3Data v3types.FungibleTokenPacketData + expPanic error + }{ + { + "success", + v1types.NewFungibleTokenPacketData("transfer/channel-0/atom", "1000", sender, receiver, ""), + v3types.NewFungibleTokenPacketData( + []*v3types.Token{ + { + Denom: "atom", + Amount: "1000", + Trace: []string{"transfer/channel-0"}, + }, + }, sender, receiver, ""), + nil, + }, + { + "success: base denom with '/'", + v1types.NewFungibleTokenPacketData("transfer/channel-0/atom/withslash", "1000", sender, receiver, ""), + v3types.NewFungibleTokenPacketData( + []*v3types.Token{ + { + Denom: "atom/withslash", + Amount: "1000", + Trace: []string{"transfer/channel-0"}, + }, + }, sender, receiver, ""), + nil, + }, + { + "success: base denom with '/' at the end", + v1types.NewFungibleTokenPacketData("transfer/channel-0/atom/", "1000", sender, receiver, ""), + v3types.NewFungibleTokenPacketData( + []*v3types.Token{ + { + Denom: "atom/", + Amount: "1000", + Trace: []string{"transfer/channel-0"}, + }, + }, sender, receiver, ""), + nil, + }, + { + "success: longer trace base denom with '/'", + v1types.NewFungibleTokenPacketData("transfer/channel-0/transfer/channel-1/atom/pool", "1000", sender, receiver, ""), + v3types.NewFungibleTokenPacketData( + []*v3types.Token{ + { + Denom: "atom/pool", + Amount: "1000", + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, sender, receiver, ""), + nil, + }, + { + "success: longer trace with non transfer port", + v1types.NewFungibleTokenPacketData("transfer/channel-0/transfer/channel-1/transfer-custom/channel-2/atom", "1000", sender, receiver, ""), + v3types.NewFungibleTokenPacketData( + []*v3types.Token{ + { + Denom: "atom", + Amount: "1000", + Trace: []string{"transfer/channel-0", "transfer/channel-1", "transfer-custom/channel-2"}, + }, + }, sender, receiver, ""), + nil, + }, + { + "success: base denom with slash, trace with non transfer port", + v1types.NewFungibleTokenPacketData("transfer/channel-0/transfer/channel-1/transfer-custom/channel-2/atom/pool", "1000", sender, receiver, ""), + v3types.NewFungibleTokenPacketData( + []*v3types.Token{ + { + Denom: "atom/pool", + Amount: "1000", + Trace: []string{"transfer/channel-0", "transfer/channel-1", "transfer-custom/channel-2"}, + }, + }, sender, receiver, ""), + nil, + }, + { + "failure: panics with empty denom", + v1types.NewFungibleTokenPacketData("", "1000", sender, receiver, ""), + v3types.FungibleTokenPacketData{}, + errorsmod.Wrap(v1types.ErrInvalidDenomForTransfer, "base denomination cannot be blank"), + }, + } + + for _, tc := range testCases { + expPass := tc.expPanic == nil + if expPass { + v3Data := PacketDataV1ToV3(tc.v1Data) + require.Equal(t, tc.v3Data, v3Data) + } else { + require.PanicsWithError(t, tc.expPanic.Error(), func() { + PacketDataV1ToV3(tc.v1Data) + }) + } + } +} diff --git a/modules/apps/transfer/internal/denom/denom.go b/modules/apps/transfer/internal/denom/denom.go new file mode 100644 index 00000000000..972528cd0c0 --- /dev/null +++ b/modules/apps/transfer/internal/denom/denom.go @@ -0,0 +1,39 @@ +package denom + +import ( + "strings" + + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" +) + +// ExtractPathAndBaseFromFullDenom returns the trace path and the base denom from +// the elements that constitute the complete denom. +func ExtractPathAndBaseFromFullDenom(fullDenomItems []string) ([]string, string) { + var ( + pathSlice []string + baseDenomSlice []string + ) + + length := len(fullDenomItems) + for i := 0; i < length; i += 2 { + // The IBC specification does not guarantee the expected format of the + // destination port or destination channel identifier. A short term solution + // to determine base denomination is to expect the channel identifier to be the + // one ibc-go specifies. A longer term solution is to separate the path and base + // denomination in the ICS20 packet. If an intermediate hop prefixes the full denom + // with a channel identifier format different from our own, the base denomination + // will be incorrectly parsed, but the token will continue to be treated correctly + // as an IBC denomination. The hash used to store the token internally on our chain + // will be the same value as the base denomination being correctly parsed. + if i < length-1 && length > 2 && channeltypes.IsValidChannelID(fullDenomItems[i+1]) { + pathSlice = append(pathSlice, fullDenomItems[i], fullDenomItems[i+1]) + } else { + baseDenomSlice = fullDenomItems[i:] + break + } + } + + baseDenom := strings.Join(baseDenomSlice, "/") + + return pathSlice, baseDenom +} diff --git a/modules/apps/transfer/types/trace.go b/modules/apps/transfer/types/trace.go index ee7b3c2ad50..00b1d3fd6f4 100644 --- a/modules/apps/transfer/types/trace.go +++ b/modules/apps/transfer/types/trace.go @@ -14,7 +14,7 @@ import ( cmtbytes "github.com/cometbft/cometbft/libs/bytes" cmttypes "github.com/cometbft/cometbft/types" - channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + denominternal "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/denom" host "github.com/cosmos/ibc-go/v8/modules/core/24-host" ) @@ -38,9 +38,9 @@ func ParseDenomTrace(rawDenom string) DenomTrace { } } - path, baseDenom := extractPathAndBaseFromFullDenom(denomSplit) + pathSlice, baseDenom := denominternal.ExtractPathAndBaseFromFullDenom(denomSplit) return DenomTrace{ - Path: path, + Path: strings.Join(pathSlice, "/"), BaseDenom: baseDenom, } } @@ -82,39 +82,6 @@ func (dt DenomTrace) IsNativeDenom() bool { return dt.Path == "" } -// extractPathAndBaseFromFullDenom returns the trace path and the base denom from -// the elements that constitute the complete denom. -func extractPathAndBaseFromFullDenom(fullDenomItems []string) (string, string) { - var ( - pathSlice []string - baseDenomSlice []string - ) - - length := len(fullDenomItems) - for i := 0; i < length; i += 2 { - // The IBC specification does not guarantee the expected format of the - // destination port or destination channel identifier. A short term solution - // to determine base denomination is to expect the channel identifier to be the - // one ibc-go specifies. A longer term solution is to separate the path and base - // denomination in the ICS20 packet. If an intermediate hop prefixes the full denom - // with a channel identifier format different from our own, the base denomination - // will be incorrectly parsed, but the token will continue to be treated correctly - // as an IBC denomination. The hash used to store the token internally on our chain - // will be the same value as the base denomination being correctly parsed. - if i < length-1 && length > 2 && channeltypes.IsValidChannelID(fullDenomItems[i+1]) { - pathSlice = append(pathSlice, fullDenomItems[i], fullDenomItems[i+1]) - } else { - baseDenomSlice = fullDenomItems[i:] - break - } - } - - path := strings.Join(pathSlice, "/") - baseDenom := strings.Join(baseDenomSlice, "/") - - return path, baseDenom -} - func validateTraceIdentifiers(identifiers []string) error { if len(identifiers) == 0 || len(identifiers)%2 != 0 { return fmt.Errorf("trace info must come in pairs of port and channel identifiers '{portID}/{channelID}', got the identifiers: %s", identifiers) @@ -199,11 +166,12 @@ func ValidatePrefixedDenom(denom string) error { return nil } - path, baseDenom := extractPathAndBaseFromFullDenom(denomSplit) + pathSlice, baseDenom := denominternal.ExtractPathAndBaseFromFullDenom(denomSplit) if strings.TrimSpace(baseDenom) == "" { return errorsmod.Wrap(ErrInvalidDenomForTransfer, "base denomination cannot be blank") } + path := strings.Join(pathSlice, "/") if path == "" { // NOTE: base denom contains slashes, so no base denomination validation return nil diff --git a/modules/apps/transfer/types/v3/packet.go b/modules/apps/transfer/types/v3/packet.go new file mode 100644 index 00000000000..44494bfdefe --- /dev/null +++ b/modules/apps/transfer/types/v3/packet.go @@ -0,0 +1,15 @@ +package v3 + +// NewFungibleTokenPacketData constructs a new FungibleTokenPacketData instance +func NewFungibleTokenPacketData( + tokens []*Token, + sender, receiver string, + memo string, +) FungibleTokenPacketData { + return FungibleTokenPacketData{ + Tokens: tokens, + Sender: sender, + Receiver: receiver, + Memo: memo, + } +} From 4e551372a1b76bda66b2eb76ecbdbb63c5608637 Mon Sep 17 00:00:00 2001 From: Charly Date: Mon, 22 Apr 2024 15:01:40 +0200 Subject: [PATCH 07/42] chore: implement required `FungibleTokenPacketData` v3 interface methods (#6126) --- modules/apps/transfer/internal/denom/denom.go | 22 + modules/apps/transfer/types/trace.go | 22 +- modules/apps/transfer/types/v3/packet.go | 101 +++- modules/apps/transfer/types/v3/packet_test.go | 436 ++++++++++++++++++ modules/apps/transfer/types/v3/token.go | 36 ++ modules/apps/transfer/types/v3/token_test.go | 140 ++++++ 6 files changed, 736 insertions(+), 21 deletions(-) create mode 100644 modules/apps/transfer/types/v3/packet_test.go create mode 100644 modules/apps/transfer/types/v3/token.go create mode 100644 modules/apps/transfer/types/v3/token_test.go diff --git a/modules/apps/transfer/internal/denom/denom.go b/modules/apps/transfer/internal/denom/denom.go index 972528cd0c0..e6b9d7b7680 100644 --- a/modules/apps/transfer/internal/denom/denom.go +++ b/modules/apps/transfer/internal/denom/denom.go @@ -1,9 +1,13 @@ package denom import ( + "fmt" "strings" + errorsmod "cosmossdk.io/errors" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v8/modules/core/24-host" ) // ExtractPathAndBaseFromFullDenom returns the trace path and the base denom from @@ -37,3 +41,21 @@ func ExtractPathAndBaseFromFullDenom(fullDenomItems []string) ([]string, string) return pathSlice, baseDenom } + +// ValidateTraceIdentifiers validates the correctness of the trace associated with a particular base denom. +func ValidateTraceIdentifiers(identifiers []string) error { + if len(identifiers) == 0 || len(identifiers)%2 != 0 { + return fmt.Errorf("trace info must come in pairs of port and channel identifiers '{portID}/{channelID}', got the identifiers: %s", identifiers) + } + + // validate correctness of port and channel identifiers + for i := 0; i < len(identifiers); i += 2 { + if err := host.PortIdentifierValidator(identifiers[i]); err != nil { + return errorsmod.Wrapf(err, "invalid port ID at position %d", i) + } + if err := host.ChannelIdentifierValidator(identifiers[i+1]); err != nil { + return errorsmod.Wrapf(err, "invalid channel ID at position %d", i) + } + } + return nil +} diff --git a/modules/apps/transfer/types/trace.go b/modules/apps/transfer/types/trace.go index 00b1d3fd6f4..ab4e7bbce72 100644 --- a/modules/apps/transfer/types/trace.go +++ b/modules/apps/transfer/types/trace.go @@ -15,7 +15,6 @@ import ( cmttypes "github.com/cometbft/cometbft/types" denominternal "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/denom" - host "github.com/cosmos/ibc-go/v8/modules/core/24-host" ) // ParseDenomTrace parses a string with the ibc prefix (denom trace) and the base denomination @@ -82,23 +81,6 @@ func (dt DenomTrace) IsNativeDenom() bool { return dt.Path == "" } -func validateTraceIdentifiers(identifiers []string) error { - if len(identifiers) == 0 || len(identifiers)%2 != 0 { - return fmt.Errorf("trace info must come in pairs of port and channel identifiers '{portID}/{channelID}', got the identifiers: %s", identifiers) - } - - // validate correctness of port and channel identifiers - for i := 0; i < len(identifiers); i += 2 { - if err := host.PortIdentifierValidator(identifiers[i]); err != nil { - return errorsmod.Wrapf(err, "invalid port ID at position %d", i) - } - if err := host.ChannelIdentifierValidator(identifiers[i+1]); err != nil { - return errorsmod.Wrapf(err, "invalid channel ID at position %d", i) - } - } - return nil -} - // Validate performs a basic validation of the DenomTrace fields. func (dt DenomTrace) Validate() error { // empty trace is accepted when token lives on the original chain @@ -112,7 +94,7 @@ func (dt DenomTrace) Validate() error { // NOTE: no base denomination validation identifiers := strings.Split(dt.Path, "/") - return validateTraceIdentifiers(identifiers) + return denominternal.ValidateTraceIdentifiers(identifiers) } // Traces defines a wrapper type for a slice of DenomTrace. @@ -178,7 +160,7 @@ func ValidatePrefixedDenom(denom string) error { } identifiers := strings.Split(path, "/") - return validateTraceIdentifiers(identifiers) + return denominternal.ValidateTraceIdentifiers(identifiers) } // ValidateIBCDenom validates that the given denomination is either: diff --git a/modules/apps/transfer/types/v3/packet.go b/modules/apps/transfer/types/v3/packet.go index 44494bfdefe..510a1024cac 100644 --- a/modules/apps/transfer/types/v3/packet.go +++ b/modules/apps/transfer/types/v3/packet.go @@ -1,6 +1,24 @@ package v3 -// NewFungibleTokenPacketData constructs a new FungibleTokenPacketData instance +import ( + "encoding/json" + "errors" + "strings" + + errorsmod "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + + "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" + ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" +) + +var ( + _ ibcexported.PacketData = (*FungibleTokenPacketData)(nil) + _ ibcexported.PacketDataProvider = (*FungibleTokenPacketData)(nil) +) + +// NewFungibleTokenPacketData constructs a new NewFungibleTokenPacketData instance func NewFungibleTokenPacketData( tokens []*Token, sender, receiver string, @@ -13,3 +31,84 @@ func NewFungibleTokenPacketData( Memo: memo, } } + +// ValidateBasic is used for validating the token transfer. +// NOTE: The addresses formats are not validated as the sender and recipient can have different +// formats defined by their corresponding chains that are not known to IBC. +func (ftpd FungibleTokenPacketData) ValidateBasic() error { + if strings.TrimSpace(ftpd.Sender) == "" { + return errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "sender address cannot be blank") + } + + if strings.TrimSpace(ftpd.Receiver) == "" { + return errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "receiver address cannot be blank") + } + + if len(ftpd.Tokens) == 0 { + return errorsmod.Wrap(types.ErrInvalidAmount, "tokens cannot be empty") + } + + for _, token := range ftpd.Tokens { + amount, ok := sdkmath.NewIntFromString(token.Amount) + if !ok { + return errorsmod.Wrapf(types.ErrInvalidAmount, "unable to parse transfer amount (%s) into math.Int", token.Amount) + } + + if !amount.IsPositive() { + return errorsmod.Wrapf(types.ErrInvalidAmount, "amount must be strictly positive: got %d", amount) + } + + if err := token.Validate(); err != nil { + return err + } + } + + if len(ftpd.Memo) > types.MaximumMemoLength { + return errorsmod.Wrapf(types.ErrInvalidMemo, "memo must not exceed %d bytes", types.MaximumMemoLength) + } + + return nil +} + +// GetBytes is a helper for serialising +func (ftpd FungibleTokenPacketData) GetBytes() []byte { + bz, err := json.Marshal(&ftpd) + if err != nil { + panic(errors.New("cannot marshal v3 FungibleTokenPacketData into bytes")) + } + + return bz +} + +// GetCustomPacketData interprets the memo field of the packet data as a JSON object +// and returns the value associated with the given key. +// If the key is missing or the memo is not properly formatted, then nil is returned. +func (ftpd FungibleTokenPacketData) GetCustomPacketData(key string) interface{} { + if len(ftpd.Memo) == 0 { + return nil + } + + jsonObject := make(map[string]interface{}) + err := json.Unmarshal([]byte(ftpd.Memo), &jsonObject) + if err != nil { + return nil + } + + memoData, found := jsonObject[key] + if !found { + return nil + } + + return memoData +} + +// GetPacketSender returns the sender address embedded in the packet data. +// +// NOTE: +// - The sender address is set by the module which requested the packet to be sent, +// and this module may not have validated the sender address by a signature check. +// - The sender address must only be used by modules on the sending chain. +// - sourcePortID is not used in this implementation. +func (ftpd FungibleTokenPacketData) GetPacketSender(sourcePortID string) string { + return ftpd.Sender +} diff --git a/modules/apps/transfer/types/v3/packet_test.go b/modules/apps/transfer/types/v3/packet_test.go new file mode 100644 index 00000000000..63435d8589e --- /dev/null +++ b/modules/apps/transfer/types/v3/packet_test.go @@ -0,0 +1,436 @@ +package v3 + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cometbft/cometbft/crypto/secp256k1" + + "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" +) + +const ( + denom = "atom/pool" + amount = "1000" + largeAmount = "18446744073709551616" // one greater than largest uint64 (^uint64(0)) + invalidLargeAmount = "115792089237316195423570985008687907853269984665640564039457584007913129639936" // 2^256 +) + +var ( + sender = secp256k1.GenPrivKey().PubKey().Address().String() + receiver = sdk.AccAddress("testaddr2").String() +) + +// TestFungibleTokenPacketDataValidateBasic tests ValidateBasic for FungibleTokenPacketData +func TestFungibleTokenPacketDataValidateBasic(t *testing.T) { + testCases := []struct { + name string + packetData FungibleTokenPacketData + expErr error + }{ + { + "success: valid packet", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "", + ), + nil, + }, + { + "success: valid packet with memo", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "memo", + ), + nil, + }, + { + "success: valid packet with large amount", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: largeAmount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "memo", + ), + nil, + }, + { + "failure: invalid denom", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: "", + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "", + ), + types.ErrInvalidDenomForTransfer, + }, + { + "failure: invalid empty amount", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: "", + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "", + ), + types.ErrInvalidAmount, + }, + { + "failure: invalid empty token array", + NewFungibleTokenPacketData( + []*Token{}, + sender, + receiver, + "", + ), + types.ErrInvalidAmount, + }, + { + "failure: invalid zero amount", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: "0", + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "", + ), + types.ErrInvalidAmount, + }, + { + "failure: invalid negative amount", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: "-100", + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "", + ), + types.ErrInvalidAmount, + }, + { + "failure: invalid large amount", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: invalidLargeAmount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "memo", + ), + types.ErrInvalidAmount, + }, + { + "failure: missing sender address", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + "", + receiver, + "memo", + ), + ibcerrors.ErrInvalidAddress, + }, + { + "failure: missing recipient address", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + "", + "", + ), + ibcerrors.ErrInvalidAddress, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tc.packetData.ValidateBasic() + + expPass := tc.expErr == nil + if expPass { + require.NoError(t, err, tc.name) + } else { + require.ErrorContains(t, err, tc.expErr.Error(), tc.name) + } + }) + } +} + +func TestGetPacketSender(t *testing.T) { + testCases := []struct { + name string + packetData FungibleTokenPacketData + expSender string + }{ + { + "non-empty sender field", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "", + ), + sender, + }, + { + "empty sender field", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + "", + receiver, + "abc", + ), + "", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + require.Equal(t, tc.expSender, tc.packetData.GetPacketSender(types.PortID)) + }) + } +} + +func TestPacketDataProvider(t *testing.T) { + testCases := []struct { + name string + packetData FungibleTokenPacketData + expCustomData interface{} + }{ + { + "success: src_callback key in memo", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + fmt.Sprintf(`{"src_callback": {"address": "%s"}}`, receiver)), + + map[string]interface{}{ + "address": receiver, + }, + }, + { + "success: src_callback key in memo with additional fields", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + fmt.Sprintf(`{"src_callback": {"address": "%s", "gas_limit": "200000"}}`, receiver)), + map[string]interface{}{ + "address": receiver, + "gas_limit": "200000", + }, + }, + { + "success: src_callback has string value", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + `{"src_callback": "string"}`), + "string", + }, + { + "failure: src_callback key not found memo", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + fmt.Sprintf(`{"dest_callback": {"address": "%s", "min_gas": "200000"}}`, receiver)), + nil, + }, + { + "failure: empty memo", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + ""), + nil, + }, + { + "failure: non-json memo", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "invalid"), + nil, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + customData := tc.packetData.GetCustomPacketData("src_callback") + require.Equal(t, tc.expCustomData, customData) + }) + } +} + +func TestFungibleTokenPacketDataOmitEmpty(t *testing.T) { + testCases := []struct { + name string + packetData FungibleTokenPacketData + expMemo bool + }{ + { + "empty memo field, resulting marshalled bytes should not contain the memo field", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "", + ), + false, + }, + { + "non-empty memo field, resulting marshalled bytes should contain the memo field", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "abc", + ), + true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + bz, err := json.Marshal(tc.packetData) + if tc.expMemo { + require.NoError(t, err, tc.name) + // check that the memo field is present in the marshalled bytes + require.Contains(t, string(bz), "memo") + } else { + require.NoError(t, err, tc.name) + // check that the memo field is not present in the marshalled bytes + require.NotContains(t, string(bz), "memo") + } + }) + } +} diff --git a/modules/apps/transfer/types/v3/token.go b/modules/apps/transfer/types/v3/token.go new file mode 100644 index 00000000000..21a658723c8 --- /dev/null +++ b/modules/apps/transfer/types/v3/token.go @@ -0,0 +1,36 @@ +package v3 + +import ( + "strings" + + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + + denominternal "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/denom" + "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" +) + +// ValidateToken validates a token denomination and trace identifiers. +func (t Token) Validate() error { + if err := sdk.ValidateDenom(t.Denom); err != nil { + return errorsmod.Wrap(types.ErrInvalidDenomForTransfer, err.Error()) + } + + trace := strings.Join(t.Trace, "/") + identifiers := strings.Split(trace, "/") + + return denominternal.ValidateTraceIdentifiers(identifiers) +} + +// GetFullDenomPath returns the full denomination according to the ICS20 specification: +// tracePath + "/" + baseDenom +// If there exists no trace then the base denomination is returned. +func (t Token) GetFullDenomPath() string { + trace := strings.Join(t.Trace, "/") + if len(trace) == 0 { + return t.Denom + } + + return strings.Join(append(t.Trace, t.Denom), "/") +} diff --git a/modules/apps/transfer/types/v3/token_test.go b/modules/apps/transfer/types/v3/token_test.go new file mode 100644 index 00000000000..13654cfcc1d --- /dev/null +++ b/modules/apps/transfer/types/v3/token_test.go @@ -0,0 +1,140 @@ +package v3 + +import ( + fmt "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" +) + +func TestGetFullDenomPath(t *testing.T) { + testCases := []struct { + name string + packetData FungibleTokenPacketData + expPath string + }{ + { + "denom path with trace", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "", + ), + "transfer/channel-0/transfer/channel-1/atom/pool", + }, + { + "nil trace", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{}, + }, + }, + sender, + receiver, + "", + ), + denom, + }, + { + "empty string trace", + NewFungibleTokenPacketData( + []*Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{""}, + }, + }, + sender, + receiver, + "", + ), + denom, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + path := tc.packetData.Tokens[0].GetFullDenomPath() + require.Equal(t, tc.expPath, path) + }) + } +} + +func TestValidate(t *testing.T) { + testCases := []struct { + name string + token Token + expError error + }{ + { + "success: multiple port channel pair denom", + Token{ + Denom: "atom", + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + nil, + }, + { + "success: one port channel pair denom", + Token{ + Denom: "uatom", + Amount: amount, + Trace: []string{"transfer/channel-1"}, + }, + nil, + }, + { + "success: non transfer port trace", + Token{ + Denom: "uatom", + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1", "transfer-custom/channel-2"}, + }, + nil, + }, + { + "failure: empty denom", + Token{ + Denom: "", + Amount: amount, + Trace: nil, + }, + types.ErrInvalidDenomForTransfer, + }, + { + "failure: invalid identifier in trace", + Token{ + Denom: "uatom", + Amount: amount, + Trace: []string{"transfer/channel-1", "randomport"}, + }, + fmt.Errorf("trace info must come in pairs of port and channel identifiers '{portID}/{channelID}', got the identifiers: [transfer channel-1 randomport]"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tc.token.Validate() + expPass := tc.expError == nil + if expPass { + require.NoError(t, err, tc.name) + } else { + require.ErrorContains(t, err, tc.expError.Error(), tc.name) + } + }) + } +} From ca056cf1fb19c696f870ad9b700fc59212eddc24 Mon Sep 17 00:00:00 2001 From: Charly Date: Mon, 29 Apr 2024 21:27:58 +0200 Subject: [PATCH 08/42] imp: `getMultiDenomFungibleTokenPacketData`to be used in packet unmarshalling/conversion (#6226) * chore: adding proto files for ics20-v2 * chore: add newline * chore: modify MsgTransfer to accept coins instead of coin * chore: reverted unintentional comment changes * chore: reverted unintentional comment changes * chore: adding test for conversion fn * chore: fix e2e linter * chore: adding docs * chore: addressing PR feedback * chore: remove duplicate import * chore: addressing PR feedback * chore: moved extration logic into internal package * chore: commented out failing test * chore: adding link to issue * chore: remove duplicate import * chore: fix linting errors * FungibleTokenPacketData interface methods + tests * linter * wip: token validation * update trace identifier validation in Token + tests * rm Printf * update with pr review * add CurrentVersion, EscrowVersion, update tests * pr review * fix e2e tests * pr review * update e2e test version * linter * update hardcoded transfer channel version from interchaintest * update hardcoded transfer channel version from interchaintest * wip packet unmarshaller * wip * wip testing * update test * linter * rm unnecessary version changes * rm unnecessary artifacts * update callbacks test * update comment * pr review * rename getMultiDenomFungibleTokenPacketData to unmarshalPacketDataBytesToICS20V2 --------- Co-authored-by: chatton Co-authored-by: Carlos Rodriguez --- modules/apps/callbacks/ibc_middleware_test.go | 18 +++- modules/apps/transfer/ibc_module.go | 31 ++++++- modules/apps/transfer/ibc_module_test.go | 88 +++++++++++++++++-- .../apps/transfer/internal/convert/convert.go | 10 +-- .../transfer/internal/convert/convert_test.go | 13 +++ 5 files changed, 140 insertions(+), 20 deletions(-) diff --git a/modules/apps/callbacks/ibc_middleware_test.go b/modules/apps/callbacks/ibc_middleware_test.go index 180ca3eb056..1b265db095c 100644 --- a/modules/apps/callbacks/ibc_middleware_test.go +++ b/modules/apps/callbacks/ibc_middleware_test.go @@ -14,6 +14,7 @@ import ( "github.com/cosmos/ibc-go/modules/apps/callbacks/types" icacontrollertypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/controller/types" transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + multidenom "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channelkeeper "github.com/cosmos/ibc-go/v8/modules/core/04-channel/keeper" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" @@ -950,14 +951,27 @@ func (s *CallbacksTestSuite) TestUnmarshalPacketData() { unmarshalerStack, ok := transferStack.(types.CallbacksCompatibleModule) s.Require().True(ok) - expPacketData := transfertypes.FungibleTokenPacketData{ + initialPacketData := transfertypes.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), Sender: ibctesting.TestAccAddress, Receiver: ibctesting.TestAccAddress, Memo: fmt.Sprintf(`{"src_callback": {"address": "%s"}, "dest_callback": {"address":"%s"}}`, ibctesting.TestAccAddress, ibctesting.TestAccAddress), } - data := expPacketData.GetBytes() + data := initialPacketData.GetBytes() + + expPacketData := multidenom.FungibleTokenPacketData{ + Tokens: []*multidenom.Token{ + { + Denom: initialPacketData.Denom, + Amount: initialPacketData.Amount, + Trace: []string{""}, + }, + }, + Sender: initialPacketData.Sender, + Receiver: initialPacketData.Receiver, + Memo: initialPacketData.Memo, + } packetData, err := unmarshalerStack.UnmarshalPacketData(data) s.Require().NoError(err) diff --git a/modules/apps/transfer/ibc_module.go b/modules/apps/transfer/ibc_module.go index 4cfd55ffdef..ed9e1526a53 100644 --- a/modules/apps/transfer/ibc_module.go +++ b/modules/apps/transfer/ibc_module.go @@ -11,8 +11,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + convertinternal "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/convert" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/keeper" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + multidenom "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" host "github.com/cosmos/ibc-go/v8/modules/core/24-host" @@ -170,6 +172,27 @@ func (IBCModule) OnChanCloseConfirm( return nil } +func (IBCModule) unmarshalPacketDataBytesToICS20V2(bz []byte) (multidenom.FungibleTokenPacketData, error) { + // TODO: remove support for this function parsing v1 packet data + // TODO: explicit check for packet data type against app version + + var datav1 types.FungibleTokenPacketData + if err := json.Unmarshal(bz, &datav1); err == nil { + if len(datav1.Denom) != 0 { + return convertinternal.PacketDataV1ToV3(datav1), nil + } + } + + var data multidenom.FungibleTokenPacketData + if err := json.Unmarshal(bz, &data); err == nil { + if len(data.Tokens) != 0 { + return data, nil + } + } + + return multidenom.FungibleTokenPacketData{}, errorsmod.Wrapf(ibcerrors.ErrInvalidType, "cannot unmarshal ICS-20 transfer packet data") +} + // OnRecvPacket implements the IBCModule interface. A successful acknowledgement // is returned if the packet data is successfully decoded and the receive application // logic returns without error. @@ -351,11 +374,11 @@ func (IBCModule) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID string, pr // UnmarshalPacketData attempts to unmarshal the provided packet data bytes // into a FungibleTokenPacketData. This function implements the optional // PacketDataUnmarshaler interface required for ADR 008 support. -func (IBCModule) UnmarshalPacketData(bz []byte) (interface{}, error) { - var packetData types.FungibleTokenPacketData - if err := json.Unmarshal(bz, &packetData); err != nil { +func (im IBCModule) UnmarshalPacketData(bz []byte) (interface{}, error) { + ftpd, err := im.unmarshalPacketDataBytesToICS20V2(bz) + if err != nil { return nil, err } - return packetData, nil + return ftpd, nil } diff --git a/modules/apps/transfer/ibc_module_test.go b/modules/apps/transfer/ibc_module_test.go index ad4b0be3861..62282b5f9a1 100644 --- a/modules/apps/transfer/ibc_module_test.go +++ b/modules/apps/transfer/ibc_module_test.go @@ -9,6 +9,7 @@ import ( capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" "github.com/cosmos/ibc-go/v8/modules/apps/transfer" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + multidenom "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" @@ -474,8 +475,8 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { sender = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String() receiver = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String() - data []byte - expPacketData types.FungibleTokenPacketData + data []byte + initialPacketData interface{} ) testCases := []struct { @@ -484,30 +485,87 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { expPass bool }{ { - "success: valid packet data with memo", + "success: valid packet data single denom -> multidenom conversion with memo", func() { - expPacketData = types.FungibleTokenPacketData{ + initialPacketData = types.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), Sender: sender, Receiver: receiver, Memo: "some memo", } - data = expPacketData.GetBytes() + + data = initialPacketData.(types.FungibleTokenPacketData).GetBytes() }, true, }, { - "success: valid packet data without memo", + "success: valid packet data single denom -> multidenom conversion without memo", func() { - expPacketData = types.FungibleTokenPacketData{ + initialPacketData = types.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), Sender: sender, Receiver: receiver, Memo: "", } - data = expPacketData.GetBytes() + + data = initialPacketData.(types.FungibleTokenPacketData).GetBytes() + }, + true, + }, + { + "success: valid packet data single denom with trace -> multidenom conversion with trace", + func() { + initialPacketData = types.FungibleTokenPacketData{ + Denom: "transfer/channel-0/atom", + Amount: ibctesting.TestCoin.Amount.String(), + Sender: sender, + Receiver: receiver, + Memo: "", + } + + data = initialPacketData.(types.FungibleTokenPacketData).GetBytes() + }, + true, + }, + { + "success: valid packet data multidenom with memo", + func() { + initialPacketData = multidenom.FungibleTokenPacketData{ + Tokens: []*multidenom.Token{ + { + Denom: "atom", + Amount: ibctesting.TestCoin.Amount.String(), + Trace: []string{"transfer/channel-0"}, + }, + }, + Sender: sender, + Receiver: receiver, + Memo: "some memo", + } + + data = initialPacketData.(multidenom.FungibleTokenPacketData).GetBytes() + }, + true, + }, + { + "success: valid packet data multidenom without memo", + func() { + initialPacketData = multidenom.FungibleTokenPacketData{ + Tokens: []*multidenom.Token{ + { + Denom: ibctesting.TestCoin.Denom, + Amount: ibctesting.TestCoin.Amount.String(), + Trace: []string{""}, + }, + }, + Sender: sender, + Receiver: receiver, + Memo: "", + } + + data = initialPacketData.(multidenom.FungibleTokenPacketData).GetBytes() }, true, }, @@ -529,7 +587,19 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { if tc.expPass { suite.Require().NoError(err) - suite.Require().Equal(expPacketData, packetData) + + v3PacketData, ok := packetData.(multidenom.FungibleTokenPacketData) + suite.Require().True(ok) + + if v1PacketData, ok := initialPacketData.(types.FungibleTokenPacketData); ok { + // Note: testing of the denom trace parsing/conversion should be done as part of testing internal conversion functions + suite.Require().Equal(v1PacketData.Amount, v3PacketData.Tokens[0].Amount) + suite.Require().Equal(v1PacketData.Sender, v3PacketData.Sender) + suite.Require().Equal(v1PacketData.Receiver, v3PacketData.Receiver) + suite.Require().Equal(v1PacketData.Memo, v3PacketData.Memo) + } else { + suite.Require().Equal(initialPacketData.(multidenom.FungibleTokenPacketData), v3PacketData) + } } else { suite.Require().Error(err) suite.Require().Nil(packetData) diff --git a/modules/apps/transfer/internal/convert/convert.go b/modules/apps/transfer/internal/convert/convert.go index 34ba1da17e6..f68a9ed808e 100644 --- a/modules/apps/transfer/internal/convert/convert.go +++ b/modules/apps/transfer/internal/convert/convert.go @@ -32,13 +32,13 @@ func PacketDataV1ToV3(packetData v1types.FungibleTokenPacketData) v3types.Fungib func extractDenomAndTraceFromV1Denom(v1Denom string) (string, []string) { v1DenomTrace := v1types.ParseDenomTrace(v1Denom) - splitPath := strings.Split(v1DenomTrace.Path, "/") - - // if the path slice is empty, then the base denom is the full native denom. - if len(splitPath) == 0 { - return v1DenomTrace.BaseDenom, nil + // if the path string is empty, then the base denom is the full native denom. + if v1DenomTrace.Path == "" { + return v1DenomTrace.BaseDenom, []string{""} } + splitPath := strings.Split(v1DenomTrace.Path, "/") + // this condition should never be reached. if len(splitPath)%2 != 0 { panic("pathSlice length is not even") diff --git a/modules/apps/transfer/internal/convert/convert_test.go b/modules/apps/transfer/internal/convert/convert_test.go index f43731ad5dd..006bf3eeb32 100644 --- a/modules/apps/transfer/internal/convert/convert_test.go +++ b/modules/apps/transfer/internal/convert/convert_test.go @@ -36,6 +36,19 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { }, sender, receiver, ""), nil, }, + { + "success with empty trace", + v1types.NewFungibleTokenPacketData("atom", "1000", sender, receiver, ""), + v3types.NewFungibleTokenPacketData( + []*v3types.Token{ + { + Denom: "atom", + Amount: "1000", + Trace: []string{""}, + }, + }, sender, receiver, ""), + nil, + }, { "success: base denom with '/'", v1types.NewFungibleTokenPacketData("transfer/channel-0/atom/withslash", "1000", sender, receiver, ""), From 147cf1789bbde4fc1b479daca94169ed2146a38d Mon Sep 17 00:00:00 2001 From: Charly Date: Mon, 6 May 2024 09:47:02 +0200 Subject: [PATCH 09/42] chore: implement version checking for channel handshake application callbacks (#6175) * add SupportedVersions array for different ics20 versions, add version checking on channel handshake application callbacks * add tests * update pr review * pr review * last few pr review nits * linter * add version counter proposing * fix missing app versino * update code + tests to return our proposed version if counterparty version is invalid * remove if statement * address review comments: return ics20-2 if counterparty version is not supported --------- Co-authored-by: Carlos Rodriguez --- modules/apps/transfer/ibc_module.go | 29 +++++---- modules/apps/transfer/ibc_module_test.go | 78 ++++++++++++++---------- modules/apps/transfer/types/keys.go | 3 + 3 files changed, 64 insertions(+), 46 deletions(-) diff --git a/modules/apps/transfer/ibc_module.go b/modules/apps/transfer/ibc_module.go index ed9e1526a53..995ba0523e1 100644 --- a/modules/apps/transfer/ibc_module.go +++ b/modules/apps/transfer/ibc_module.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "math" + "slices" "strings" errorsmod "cosmossdk.io/errors" @@ -87,12 +88,13 @@ func (im IBCModule) OnChanOpenInit( return "", err } + // default to latest supported version if strings.TrimSpace(version) == "" { version = types.Version } - if version != types.Version { - return "", errorsmod.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, version) + if !slices.Contains(types.SupportedVersions, version) { + return "", errorsmod.Wrapf(types.ErrInvalidVersion, "expected one of %s, got %s", types.SupportedVersions, version) } // Claim channel capability passed back by IBC module @@ -118,8 +120,8 @@ func (im IBCModule) OnChanOpenTry( return "", err } - if counterpartyVersion != types.Version { - return "", errorsmod.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: expected %s, got %s", types.Version, counterpartyVersion) + if !slices.Contains(types.SupportedVersions, counterpartyVersion) { + return types.Version, nil } // OpenTry must claim the channelCapability that IBC passes into the callback @@ -127,7 +129,7 @@ func (im IBCModule) OnChanOpenTry( return "", err } - return types.Version, nil + return counterpartyVersion, nil } // OnChanOpenAck implements the IBCModule interface @@ -138,9 +140,10 @@ func (IBCModule) OnChanOpenAck( _ string, counterpartyVersion string, ) error { - if counterpartyVersion != types.Version { - return errorsmod.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: expected %s, got %s", types.Version, counterpartyVersion) + if !slices.Contains(types.SupportedVersions, counterpartyVersion) { + return errorsmod.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: expected one of %s, got %s", types.SupportedVersions, counterpartyVersion) } + return nil } @@ -338,8 +341,8 @@ func (im IBCModule) OnChanUpgradeInit(ctx sdk.Context, portID, channelID string, return "", err } - if proposedVersion != types.Version { - return "", errorsmod.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, proposedVersion) + if !slices.Contains(types.SupportedVersions, proposedVersion) { + return "", errorsmod.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: expected one of %s, got %s", types.SupportedVersions, proposedVersion) } return proposedVersion, nil @@ -351,8 +354,8 @@ func (im IBCModule) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, return "", err } - if counterpartyVersion != types.Version { - return "", errorsmod.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, counterpartyVersion) + if !slices.Contains(types.SupportedVersions, counterpartyVersion) { + return types.Version, nil } return counterpartyVersion, nil @@ -360,8 +363,8 @@ func (im IBCModule) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, // OnChanUpgradeAck implements the IBCModule interface func (IBCModule) OnChanUpgradeAck(ctx sdk.Context, portID, channelID, counterpartyVersion string) error { - if counterpartyVersion != types.Version { - return errorsmod.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, counterpartyVersion) + if !slices.Contains(types.SupportedVersions, counterpartyVersion) { + return errorsmod.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: expected one of %s, got %s", types.SupportedVersions, counterpartyVersion) } return nil diff --git a/modules/apps/transfer/ibc_module_test.go b/modules/apps/transfer/ibc_module_test.go index 62282b5f9a1..4acb9b6501a 100644 --- a/modules/apps/transfer/ibc_module_test.go +++ b/modules/apps/transfer/ibc_module_test.go @@ -26,50 +26,56 @@ func (suite *TransferTestSuite) TestOnChanOpenInit() { ) testCases := []struct { - name string - malleate func() - expPass bool + name string + malleate func() + expPass bool + expVersion string }{ { - "success", func() {}, true, + "success", func() {}, true, types.Version, }, { // connection hops is not used in the transfer application callback, // it is already validated in the core OnChanUpgradeInit. "success: invalid connection hops", func() { path.EndpointA.ConnectionID = "invalid-connection-id" - }, true, + }, true, types.Version, }, { - "empty version string", func() { + "success: empty version string", func() { channel.Version = "" - }, true, + }, true, types.Version, + }, + { + "success: ics20-1 legacy", func() { + channel.Version = types.Version1 + }, true, types.Version1, }, { "max channels reached", func() { path.EndpointA.ChannelID = channeltypes.FormatChannelIdentifier(math.MaxUint32 + 1) - }, false, + }, false, "", }, { "invalid order - ORDERED", func() { channel.Ordering = channeltypes.ORDERED - }, false, + }, false, "", }, { "invalid port ID", func() { path.EndpointA.ChannelConfig.PortID = ibctesting.MockPort - }, false, + }, false, "", }, { "invalid version", func() { channel.Version = "version" //nolint:goconst - }, false, + }, false, "", }, { "capability already claimed", func() { err := suite.chainA.GetSimApp().ScopedTransferKeeper.ClaimCapability(suite.chainA.GetContext(), chanCap, host.ChannelCapabilityPath(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)) suite.Require().NoError(err) - }, false, + }, false, "", }, } @@ -104,7 +110,7 @@ func (suite *TransferTestSuite) TestOnChanOpenInit() { if tc.expPass { suite.Require().NoError(err) - suite.Require().Equal(types.Version, version) + suite.Require().Equal(tc.expVersion, version) } else { suite.Require().Error(err) suite.Require().Equal(version, "") @@ -123,38 +129,44 @@ func (suite *TransferTestSuite) TestOnChanOpenTry() { ) testCases := []struct { - name string - malleate func() - expPass bool + name string + malleate func() + expPass bool + expVersion string }{ { - "success", func() {}, true, + "success", func() {}, true, types.Version, + }, + { + "success: counterparty version is legacy ics20-1", func() { + counterpartyVersion = types.Version1 + }, true, types.Version1, + }, + { + "success: invalid counterparty version, we use our proposed version", func() { + counterpartyVersion = "version" + }, true, types.Version, }, { "max channels reached", func() { path.EndpointA.ChannelID = channeltypes.FormatChannelIdentifier(math.MaxUint32 + 1) - }, false, + }, false, "", }, { "capability already claimed", func() { err := suite.chainA.GetSimApp().ScopedTransferKeeper.ClaimCapability(suite.chainA.GetContext(), chanCap, host.ChannelCapabilityPath(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)) suite.Require().NoError(err) - }, false, + }, false, "", }, { "invalid order - ORDERED", func() { channel.Ordering = channeltypes.ORDERED - }, false, + }, false, "", }, { "invalid port ID", func() { path.EndpointA.ChannelConfig.PortID = ibctesting.MockPort - }, false, - }, - { - "invalid counterparty version", func() { - counterpartyVersion = "version" - }, false, + }, false, "", }, } @@ -195,7 +207,7 @@ func (suite *TransferTestSuite) TestOnChanOpenTry() { if tc.expPass { suite.Require().NoError(err) - suite.Require().Equal(types.Version, version) + suite.Require().Equal(tc.expVersion, version) } else { suite.Require().Error(err) suite.Require().Equal("", version) @@ -340,18 +352,18 @@ func (suite *TransferTestSuite) TestOnChanUpgradeTry() { nil, }, { - "invalid upgrade ordering", + "success: invalid upgrade version from counterparty, we use our proposed version", func() { - counterpartyUpgrade.Fields.Ordering = channeltypes.ORDERED + counterpartyUpgrade.Fields.Version = ibctesting.InvalidID }, - channeltypes.ErrInvalidChannelOrdering, + nil, }, { - "invalid upgrade version", + "invalid upgrade ordering", func() { - counterpartyUpgrade.Fields.Version = ibctesting.InvalidID + counterpartyUpgrade.Fields.Ordering = channeltypes.ORDERED }, - types.ErrInvalidVersion, + channeltypes.ErrInvalidChannelOrdering, }, } diff --git a/modules/apps/transfer/types/keys.go b/modules/apps/transfer/types/keys.go index 8db04bb87bb..d985438ab71 100644 --- a/modules/apps/transfer/types/keys.go +++ b/modules/apps/transfer/types/keys.go @@ -51,6 +51,9 @@ var ( PortKey = []byte{0x01} // DenomTraceKey defines the key to store the denomination trace info in store DenomTraceKey = []byte{0x02} + + // SupportedVersions defines all versions that are supported by the module + SupportedVersions = []string{Version, Version1} ) // GetEscrowAddress returns the escrow address for the specified channel. From 9bbfa1a5f1cfae05889ca832f22271352d49d429 Mon Sep 17 00:00:00 2001 From: Charly Date: Mon, 6 May 2024 09:51:01 +0200 Subject: [PATCH 10/42] imp: update transfer authz implementation to account for multi denom transfers (#6252) * add transfer authz code + tests * linter * address review comments --------- Co-authored-by: Carlos Rodriguez --- .../transfer/types/transfer_authorization.go | 84 ++++++++++++------- .../types/transfer_authorization_test.go | 27 ++++-- 2 files changed, 76 insertions(+), 35 deletions(-) diff --git a/modules/apps/transfer/types/transfer_authorization.go b/modules/apps/transfer/types/transfer_authorization.go index b38702f980a..e5612c6442e 100644 --- a/modules/apps/transfer/types/transfer_authorization.go +++ b/modules/apps/transfer/types/transfer_authorization.go @@ -22,6 +22,10 @@ import ( var _ authz.Authorization = (*TransferAuthorization)(nil) +const ( + allocationNotFound = -1 +) + // maxUint256 is the maximum value for a 256 bit unsigned integer. var maxUint256 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1)) @@ -38,48 +42,47 @@ func (TransferAuthorization) MsgTypeURL() string { } // Accept implements Authorization.Accept. -func (a TransferAuthorization) Accept(ctx context.Context, msg proto.Message) (authz.AcceptResponse, error) { +func (a TransferAuthorization) Accept(goCtx context.Context, msg proto.Message) (authz.AcceptResponse, error) { msgTransfer, ok := msg.(*MsgTransfer) if !ok { return authz.AcceptResponse{}, errorsmod.Wrap(ibcerrors.ErrInvalidType, "type mismatch") } - // TODO: replace with correct usage in https://github.com/cosmos/ibc-go/issues/5802 - token := msgTransfer.GetTokens()[0] + index := getAllocationIndex(*msgTransfer, a.Allocations) + if index == allocationNotFound { + return authz.AcceptResponse{}, errorsmod.Wrapf(ibcerrors.ErrNotFound, "requested port and channel allocation does not exist") + } - for index, allocation := range a.Allocations { - if !(allocation.SourceChannel == msgTransfer.SourceChannel && allocation.SourcePort == msgTransfer.SourcePort) { - continue - } + allocation := a.Allocations[index] + ctx := sdk.UnwrapSDKContext(goCtx) - if !isAllowedAddress(sdk.UnwrapSDKContext(ctx), msgTransfer.Receiver, allocation.AllowList) { - return authz.AcceptResponse{}, errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "not allowed receiver address for transfer") - } + if !isAllowedAddress(ctx, msgTransfer.Receiver, allocation.AllowList) { + return authz.AcceptResponse{}, errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "not allowed receiver address for transfer") + } - err := validateMemo(sdk.UnwrapSDKContext(ctx), msgTransfer.Memo, allocation.AllowedPacketData) - if err != nil { - return authz.AcceptResponse{}, err - } + err := validateMemo(ctx, msgTransfer.Memo, allocation.AllowedPacketData) + if err != nil { + return authz.AcceptResponse{}, err + } + // bool flag to see if we have updated any of the allocations + allocationModified := false + + // update spend limit for each token in the MsgTransfer + for _, token := range msgTransfer.GetTokens() { // If the spend limit is set to the MaxUint256 sentinel value, do not subtract the amount from the spend limit. + // if there is no unlimited spend, then we need to subtract the amount from the spend limit to get the limit left if allocation.SpendLimit.AmountOf(token.Denom).Equal(UnboundedSpendLimit()) { - return authz.AcceptResponse{Accept: true, Delete: false, Updated: nil}, nil + continue } - limitLeft, isNegative := allocation.SpendLimit.SafeSub(token) + limitLeft, isNegative := a.Allocations[index].SpendLimit.SafeSub(token) if isNegative { - return authz.AcceptResponse{}, errorsmod.Wrapf(ibcerrors.ErrInsufficientFunds, "requested amount is more than spend limit") + return authz.AcceptResponse{}, errorsmod.Wrapf(ibcerrors.ErrInsufficientFunds, "requested amount of token %s is more than spend limit", token.Denom) } - if limitLeft.IsZero() { - a.Allocations = append(a.Allocations[:index], a.Allocations[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 - } + allocationModified = true + a.Allocations[index] = Allocation{ SourcePort: allocation.SourcePort, SourceChannel: allocation.SourceChannel, @@ -87,13 +90,24 @@ func (a TransferAuthorization) Accept(ctx context.Context, msg proto.Message) (a AllowList: allocation.AllowList, AllowedPacketData: allocation.AllowedPacketData, } + } + + // if the spend limit is zero of the associated allocation then we delete it. + if a.Allocations[index].SpendLimit.IsZero() { + a.Allocations = append(a.Allocations[:index], a.Allocations[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 + if !allocationModified { + return authz.AcceptResponse{Accept: true, Delete: false, Updated: nil}, nil } - return authz.AcceptResponse{}, errorsmod.Wrapf(ibcerrors.ErrNotFound, "requested port and channel allocation does not exist") + return authz.AcceptResponse{Accept: true, Delete: false, Updated: &TransferAuthorization{ + Allocations: a.Allocations, + }}, nil } // ValidateBasic implements Authorization.ValidateBasic. @@ -212,3 +226,13 @@ func validateMemo(ctx sdk.Context, memo string, allowedPacketDataList []string) func UnboundedSpendLimit() sdkmath.Int { return sdkmath.NewIntFromBigInt(maxUint256) } + +// getAllocationIndex ranges through a set of allocations, and returns the index of the allocation if found. If not, returns -1. +func getAllocationIndex(msg MsgTransfer, allocations []Allocation) int { + for index, allocation := range allocations { + if allocation.SourceChannel == msg.SourceChannel && allocation.SourcePort == msg.SourcePort { + return index + } + } + return allocationNotFound +} diff --git a/modules/apps/transfer/types/transfer_authorization_test.go b/modules/apps/transfer/types/transfer_authorization_test.go index d86c396d335..063200c9d92 100644 --- a/modules/apps/transfer/types/transfer_authorization_test.go +++ b/modules/apps/transfer/types/transfer_authorization_test.go @@ -67,15 +67,32 @@ func (suite *TypesTestSuite) TestTransferAuthorizationAccept() { }, }, { - "success: with multiple allocations", + "success: with multiple allocations and multidenom transfer", func() { - alloc := types.Allocation{ + coins := sdk.NewCoins( + ibctesting.TestCoin, + sdk.NewCoin("atom", sdkmath.NewInt(100)), + sdk.NewCoin("osmo", sdkmath.NewInt(100)), + ) + + allocation := types.Allocation{ SourcePort: ibctesting.MockPort, SourceChannel: "channel-9", - SpendLimit: ibctesting.TestCoins, + SpendLimit: coins, } - transferAuthz.Allocations = append(transferAuthz.Allocations, alloc) + transferAuthz.Allocations = append(transferAuthz.Allocations, allocation) + + msgTransfer = types.NewMsgTransfer( + ibctesting.MockPort, + "channel-9", + coins, + suite.chainA.SenderAccount.GetAddress().String(), + ibctesting.TestAccAddress, + suite.chainB.GetTimeoutHeight(), + 0, + "", + ) }, func(res authz.AcceptResponse, err error) { suite.Require().NoError(err) @@ -86,7 +103,7 @@ func (suite *TypesTestSuite) TestTransferAuthorizationAccept() { updatedAuthz, ok := res.Updated.(*types.TransferAuthorization) suite.Require().True(ok) - // assert spent spendlimit is removed from the list + // assert spent spendlimits are removed from the list suite.Require().Len(updatedAuthz.Allocations, 1) }, }, From 4f57916f000ce36e9a6a9ab5c07a63f85c11ff31 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Fri, 10 May 2024 10:42:22 +0200 Subject: [PATCH 11/42] ics20-v2: backwards compatibility for transfer rpc and packet callbacks (#6189) * chore: adding proto files for ics20-v2 * chore: add newline * chore: modify MsgTransfer to accept coins instead of coin * chore: reverted unintentional comment changes * chore: reverted unintentional comment changes * chore: adding test for conversion fn * chore: fix e2e linter * chore: adding docs * chore: addressing PR feedback * chore: remove duplicate import * chore: addressing PR feedback * chore: moved extration logic into internal package * chore: commented out failing test * chore: adding link to issue * chore: remove duplicate import * chore: fix linting errors * FungibleTokenPacketData interface methods + tests * linter * wip: token validation * update trace identifier validation in Token + tests * rm Printf * update with pr review * pr review * linter * rm unused function: linter * wip pr feedback * fix test * pr review * lintttttt * wip: backwards compatibility for transfer rpc * implement changes for ics20-v2 packet data for onRecvPacket, onAcknowledgePacket and onTimeoutPacket * fix callbacks tests * lint --------- Co-authored-by: chatton Co-authored-by: Charly --- modules/apps/callbacks/ibc_middleware_test.go | 105 +++-- modules/apps/transfer/ibc_module.go | 109 +++-- .../apps/transfer/internal/convert/convert.go | 12 +- .../transfer/internal/convert/convert_test.go | 28 +- .../apps/transfer/keeper/mbt_relay_test.go | 25 +- modules/apps/transfer/keeper/msg_server.go | 48 ++- .../apps/transfer/keeper/msg_server_test.go | 150 +++++-- modules/apps/transfer/keeper/relay.go | 379 ++++++++++-------- modules/apps/transfer/keeper/relay_test.go | 132 +++--- modules/apps/transfer/types/events.go | 1 + modules/apps/transfer/types/v3/token.go | 4 + testing/solomachine.go | 23 +- 12 files changed, 635 insertions(+), 381 deletions(-) diff --git a/modules/apps/callbacks/ibc_middleware_test.go b/modules/apps/callbacks/ibc_middleware_test.go index 1b265db095c..c08f531ca3d 100644 --- a/modules/apps/callbacks/ibc_middleware_test.go +++ b/modules/apps/callbacks/ibc_middleware_test.go @@ -13,8 +13,8 @@ import ( "github.com/cosmos/ibc-go/modules/apps/callbacks/testing/simapp" "github.com/cosmos/ibc-go/modules/apps/callbacks/types" icacontrollertypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/controller/types" - transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" - multidenom "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" + transferv1types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + transferv3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channelkeeper "github.com/cosmos/ibc-go/v8/modules/core/04-channel/keeper" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" @@ -94,7 +94,7 @@ func (s *CallbacksTestSuite) TestWithICS4Wrapper() { } func (s *CallbacksTestSuite) TestSendPacket() { - var packetData transfertypes.FungibleTokenPacketData + var packetData transferv3types.FungibleTokenPacketData testCases := []struct { name string @@ -165,9 +165,17 @@ func (s *CallbacksTestSuite) TestSendPacket() { transferICS4Wrapper := GetSimApp(s.chainA).TransferKeeper.GetICS4Wrapper() - packetData = transfertypes.NewFungibleTokenPacketData( - ibctesting.TestCoin.GetDenom(), ibctesting.TestCoin.Amount.String(), ibctesting.TestAccAddress, - ibctesting.TestAccAddress, fmt.Sprintf(`{"src_callback": {"address": "%s"}}`, simapp.SuccessContract), + packetData = transferv3types.NewFungibleTokenPacketData( + []*transferv3types.Token{ + { + Denom: ibctesting.TestCoin.GetDenom(), + Amount: ibctesting.TestCoin.Amount.String(), + Trace: []string{}, + }, + }, + ibctesting.TestAccAddress, + ibctesting.TestAccAddress, + fmt.Sprintf(`{"src_callback": {"address": "%s"}}`, simapp.SuccessContract), ) chanCap := s.path.EndpointA.Chain.GetChannelCapability(s.path.EndpointA.ChannelConfig.PortID, s.path.EndpointA.ChannelID) @@ -223,7 +231,7 @@ func (s *CallbacksTestSuite) TestOnAcknowledgementPacket() { ) var ( - packetData transfertypes.FungibleTokenPacketData + packetData transferv3types.FungibleTokenPacketData packet channeltypes.Packet ack []byte ctx sdk.Context @@ -299,8 +307,16 @@ func (s *CallbacksTestSuite) TestOnAcknowledgementPacket() { s.SetupTransferTest() userGasLimit = 600000 - packetData = transfertypes.NewFungibleTokenPacketData( - ibctesting.TestCoin.GetDenom(), ibctesting.TestCoin.Amount.String(), ibctesting.TestAccAddress, ibctesting.TestAccAddress, + packetData = transferv3types.NewFungibleTokenPacketData( + []*transferv3types.Token{ + { + Denom: ibctesting.TestCoin.GetDenom(), + Amount: ibctesting.TestCoin.Amount.String(), + Trace: []string{}, + }, + }, + ibctesting.TestAccAddress, + ibctesting.TestAccAddress, fmt.Sprintf(`{"src_callback": {"address":"%s", "gas_limit":"%d"}}`, simapp.SuccessContract, userGasLimit), ) @@ -323,7 +339,7 @@ func (s *CallbacksTestSuite) TestOnAcknowledgementPacket() { tc.malleate() // callbacks module is routed as top level middleware - transferStack, ok := s.chainA.App.GetIBCKeeper().Router.GetRoute(transfertypes.ModuleName) + transferStack, ok := s.chainA.App.GetIBCKeeper().Router.GetRoute(transferv1types.ModuleName) s.Require().True(ok) onAcknowledgementPacket := func() error { @@ -385,7 +401,7 @@ func (s *CallbacksTestSuite) TestOnTimeoutPacket() { ) var ( - packetData transfertypes.FungibleTokenPacketData + packetData transferv3types.FungibleTokenPacketData packet channeltypes.Packet ctx sdk.Context ) @@ -408,7 +424,7 @@ func (s *CallbacksTestSuite) TestOnTimeoutPacket() { packet.Data = []byte("invalid packet data") }, noExecution, - ibcerrors.ErrUnknownRequest, + ibcerrors.ErrInvalidType, }, { "success: no-op on callback data is not valid", @@ -462,7 +478,7 @@ func (s *CallbacksTestSuite) TestOnTimeoutPacket() { // succeed on timeout userGasLimit := 600_000 timeoutTimestamp := uint64(s.chainB.GetContext().BlockTime().UnixNano()) - msg := transfertypes.NewMsgTransfer( + msg := transferv1types.NewMsgTransfer( s.path.EndpointA.ChannelConfig.PortID, s.path.EndpointA.ChannelID, sdk.NewCoins(ibctesting.TestCoin), s.chainA.SenderAccount.GetAddress().String(), s.chainB.SenderAccount.GetAddress().String(), clienttypes.ZeroHeight(), timeoutTimestamp, @@ -486,7 +502,7 @@ func (s *CallbacksTestSuite) TestOnTimeoutPacket() { tc.malleate() // callbacks module is routed as top level middleware - transferStack, ok := s.chainA.App.GetIBCKeeper().Router.GetRoute(transfertypes.ModuleName) + transferStack, ok := s.chainA.App.GetIBCKeeper().Router.GetRoute(transferv1types.ModuleName) s.Require().True(ok) onTimeoutPacket := func() error { @@ -547,7 +563,7 @@ func (s *CallbacksTestSuite) TestOnRecvPacket() { ) var ( - packetData transfertypes.FungibleTokenPacketData + packetData transferv3types.FungibleTokenPacketData packet channeltypes.Packet ctx sdk.Context userGasLimit uint64 @@ -624,8 +640,16 @@ func (s *CallbacksTestSuite) TestOnRecvPacket() { // set user gas limit above panic level in mock contract keeper userGasLimit = 600_000 - packetData = transfertypes.NewFungibleTokenPacketData( - ibctesting.TestCoin.GetDenom(), ibctesting.TestCoin.Amount.String(), ibctesting.TestAccAddress, s.chainB.SenderAccount.GetAddress().String(), + packetData = transferv3types.NewFungibleTokenPacketData( + []*transferv3types.Token{ + { + Denom: ibctesting.TestCoin.GetDenom(), + Amount: ibctesting.TestCoin.Amount.String(), + Trace: []string{}, + }, + }, + ibctesting.TestAccAddress, + s.chainB.SenderAccount.GetAddress().String(), fmt.Sprintf(`{"dest_callback": {"address":"%s", "gas_limit":"%d"}}`, ibctesting.TestAccAddress, userGasLimit), ) @@ -646,7 +670,7 @@ func (s *CallbacksTestSuite) TestOnRecvPacket() { tc.malleate() // callbacks module is routed as top level middleware - transferStack, ok := s.chainB.App.GetIBCKeeper().Router.GetRoute(transfertypes.ModuleName) + transferStack, ok := s.chainB.App.GetIBCKeeper().Router.GetRoute(transferv1types.ModuleName) s.Require().True(ok) onRecvPacket := func() ibcexported.Acknowledgement { @@ -701,7 +725,7 @@ func (s *CallbacksTestSuite) TestOnRecvPacket() { func (s *CallbacksTestSuite) TestWriteAcknowledgement() { var ( - packetData transfertypes.FungibleTokenPacketData + packetData transferv3types.FungibleTokenPacketData packet channeltypes.Packet ctx sdk.Context ack ibcexported.Acknowledgement @@ -748,8 +772,16 @@ func (s *CallbacksTestSuite) TestWriteAcknowledgement() { s.SetupTransferTest() // set user gas limit above panic level in mock contract keeper - packetData = transfertypes.NewFungibleTokenPacketData( - ibctesting.TestCoin.GetDenom(), ibctesting.TestCoin.Amount.String(), ibctesting.TestAccAddress, s.chainB.SenderAccount.GetAddress().String(), + packetData = transferv3types.NewFungibleTokenPacketData( + []*transferv3types.Token{ + { + Denom: ibctesting.TestCoin.GetDenom(), + Amount: ibctesting.TestCoin.Amount.String(), + Trace: []string{}, + }, + }, + ibctesting.TestAccAddress, + s.chainB.SenderAccount.GetAddress().String(), fmt.Sprintf(`{"dest_callback": {"address":"%s", "gas_limit":"600000"}}`, ibctesting.TestAccAddress), ) @@ -945,37 +977,44 @@ func (s *CallbacksTestSuite) TestUnmarshalPacketData() { // We will pass the function call down the transfer stack to the transfer module // transfer stack UnmarshalPacketData call order: callbacks -> fee -> transfer - transferStack, ok := s.chainA.App.GetIBCKeeper().Router.GetRoute(transfertypes.ModuleName) + transferStack, ok := s.chainA.App.GetIBCKeeper().Router.GetRoute(transferv1types.ModuleName) s.Require().True(ok) unmarshalerStack, ok := transferStack.(types.CallbacksCompatibleModule) s.Require().True(ok) - initialPacketData := transfertypes.FungibleTokenPacketData{ + expPacketDataICS20V1 := transferv1types.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), Sender: ibctesting.TestAccAddress, Receiver: ibctesting.TestAccAddress, Memo: fmt.Sprintf(`{"src_callback": {"address": "%s"}, "dest_callback": {"address":"%s"}}`, ibctesting.TestAccAddress, ibctesting.TestAccAddress), } - data := initialPacketData.GetBytes() - expPacketData := multidenom.FungibleTokenPacketData{ - Tokens: []*multidenom.Token{ + expPacketDataICS20V2 := transferv3types.FungibleTokenPacketData{ + Tokens: []*transferv3types.Token{ { - Denom: initialPacketData.Denom, - Amount: initialPacketData.Amount, - Trace: []string{""}, + Denom: ibctesting.TestCoin.Denom, + Amount: ibctesting.TestCoin.Amount.String(), + Trace: nil, }, }, - Sender: initialPacketData.Sender, - Receiver: initialPacketData.Receiver, - Memo: initialPacketData.Memo, + Sender: ibctesting.TestAccAddress, + Receiver: ibctesting.TestAccAddress, + Memo: fmt.Sprintf(`{"src_callback": {"address": "%s"}, "dest_callback": {"address":"%s"}}`, ibctesting.TestAccAddress, ibctesting.TestAccAddress), } + // Unmarshal ICS20 v1 packet data + data := expPacketDataICS20V1.GetBytes() packetData, err := unmarshalerStack.UnmarshalPacketData(data) s.Require().NoError(err) - s.Require().Equal(expPacketData, packetData) + s.Require().Equal(expPacketDataICS20V2, packetData) + + // Unmarshal ICS20 v1 packet data + data = expPacketDataICS20V2.GetBytes() + packetData, err = unmarshalerStack.UnmarshalPacketData(data) + s.Require().NoError(err) + s.Require().Equal(expPacketDataICS20V2, packetData) } func (s *CallbacksTestSuite) TestGetAppVersion() { diff --git a/modules/apps/transfer/ibc_module.go b/modules/apps/transfer/ibc_module.go index 995ba0523e1..595c014f00b 100644 --- a/modules/apps/transfer/ibc_module.go +++ b/modules/apps/transfer/ibc_module.go @@ -207,10 +207,8 @@ func (im IBCModule) OnRecvPacket( logger := im.keeper.Logger(ctx) ack := channeltypes.NewResultAcknowledgement([]byte{byte(1)}) - var data types.FungibleTokenPacketData - var ackErr error - if err := json.Unmarshal(packet.GetData(), &data); err != nil { - ackErr = errorsmod.Wrapf(ibcerrors.ErrInvalidType, "cannot unmarshal ICS-20 transfer packet data") + data, ackErr := im.unmarshalPacketDataBytesToICS20V2(packet.GetData()) + if ackErr != nil { logger.Error(fmt.Sprintf("%s sequence %d", ackErr.Error(), packet.Sequence)) ack = channeltypes.NewErrorAcknowledgement(ackErr) } @@ -228,26 +226,32 @@ func (im IBCModule) OnRecvPacket( } } + events := make([]sdk.Event, 0, len(data.Tokens)+1) + for _, token := range data.Tokens { + events = append(events, sdk.NewEvent( + types.EventTypePacket, + sdk.NewAttribute(types.AttributeKeySender, data.Sender), + sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), + sdk.NewAttribute(types.AttributeKeyDenom, token.GetFullDenomPath()), + sdk.NewAttribute(types.AttributeKeyAmount, token.Amount), + sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), + )) + } + eventAttributes := []sdk.Attribute{ sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), - sdk.NewAttribute(sdk.AttributeKeySender, data.Sender), - sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), - sdk.NewAttribute(types.AttributeKeyDenom, data.Denom), - sdk.NewAttribute(types.AttributeKeyAmount, data.Amount), - sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), sdk.NewAttribute(types.AttributeKeyAckSuccess, fmt.Sprintf("%t", ack.Success())), } - if ackErr != nil { eventAttributes = append(eventAttributes, sdk.NewAttribute(types.AttributeKeyAckError, ackErr.Error())) } - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypePacket, - eventAttributes..., - ), - ) + events = append(events, sdk.NewEvent( + sdk.EventTypeMessage, + eventAttributes..., + )) + + ctx.EventManager().EmitEvents(events) // NOTE: acknowledgement will be written synchronously during IBC handler execution. return ack @@ -264,27 +268,35 @@ func (im IBCModule) OnAcknowledgementPacket( if err := types.ModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil { return errorsmod.Wrapf(ibcerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet acknowledgement: %v", err) } - var data types.FungibleTokenPacketData - if err := json.Unmarshal(packet.GetData(), &data); err != nil { - return errorsmod.Wrapf(ibcerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data: %s", err.Error()) + + data, err := im.unmarshalPacketDataBytesToICS20V2(packet.GetData()) + if err != nil { + return err } if err := im.keeper.OnAcknowledgementPacket(ctx, packet, data, ack); err != nil { return err } - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypePacket, - sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), - sdk.NewAttribute(sdk.AttributeKeySender, data.Sender), - sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), - sdk.NewAttribute(types.AttributeKeyDenom, data.Denom), - sdk.NewAttribute(types.AttributeKeyAmount, data.Amount), - sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), - sdk.NewAttribute(types.AttributeKeyAck, ack.String()), - ), - ) + events := make([]sdk.Event, 0, len(data.Tokens)+1) + for _, token := range data.Tokens { + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypePacket, + sdk.NewAttribute(sdk.AttributeKeySender, data.Sender), + sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), + sdk.NewAttribute(types.AttributeKeyDenom, token.GetFullDenomPath()), + sdk.NewAttribute(types.AttributeKeyAmount, token.Amount), + sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), + ), + ) + } + events = append(events, sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + sdk.NewAttribute(types.AttributeKeyAck, ack.String()), + )) + ctx.EventManager().EmitEvents(events) switch resp := ack.Response.(type) { case *channeltypes.Acknowledgement_Result: @@ -312,25 +324,34 @@ func (im IBCModule) OnTimeoutPacket( packet channeltypes.Packet, relayer sdk.AccAddress, ) error { - var data types.FungibleTokenPacketData - if err := json.Unmarshal(packet.GetData(), &data); err != nil { - return errorsmod.Wrapf(ibcerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data: %s", err.Error()) + data, err := im.unmarshalPacketDataBytesToICS20V2(packet.GetData()) + if err != nil { + return err } + // refund tokens if err := im.keeper.OnTimeoutPacket(ctx, packet, data); err != nil { return err } - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeTimeout, - sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), - sdk.NewAttribute(types.AttributeKeyRefundReceiver, data.Sender), - sdk.NewAttribute(types.AttributeKeyRefundDenom, data.Denom), - sdk.NewAttribute(types.AttributeKeyRefundAmount, data.Amount), - sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), - ), - ) + events := make([]sdk.Event, 0, len(data.Tokens)+1) + for _, token := range data.Tokens { + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeTimeout, + sdk.NewAttribute(sdk.AttributeKeySender, data.Sender), + sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), + sdk.NewAttribute(types.AttributeKeyDenom, token.GetFullDenomPath()), + sdk.NewAttribute(types.AttributeKeyAmount, token.Amount), + sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), + ), + ) + } + events = append(events, sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + )) + ctx.EventManager().EmitEvents(events) return nil } diff --git a/modules/apps/transfer/internal/convert/convert.go b/modules/apps/transfer/internal/convert/convert.go index f68a9ed808e..2665974d64c 100644 --- a/modules/apps/transfer/internal/convert/convert.go +++ b/modules/apps/transfer/internal/convert/convert.go @@ -3,17 +3,17 @@ package convert import ( "strings" - v1types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" ) // PacketDataV1ToV3 converts a v1 (ICS20-V1) packet data to a v3 (ICS20-V2) packet data. -func PacketDataV1ToV3(packetData v1types.FungibleTokenPacketData) v3types.FungibleTokenPacketData { +func PacketDataV1ToV3(packetData types.FungibleTokenPacketData) v3types.FungibleTokenPacketData { if err := packetData.ValidateBasic(); err != nil { panic(err) } - v2Denom, trace := extractDenomAndTraceFromV1Denom(packetData.Denom) + v2Denom, trace := ExtractDenomAndTraceFromV1Denom(packetData.Denom) return v3types.FungibleTokenPacketData{ Tokens: []*v3types.Token{ { @@ -29,12 +29,12 @@ func PacketDataV1ToV3(packetData v1types.FungibleTokenPacketData) v3types.Fungib } // extractDenomAndTraceFromV1Denom extracts the base denom and remaining trace from a v1 IBC denom. -func extractDenomAndTraceFromV1Denom(v1Denom string) (string, []string) { - v1DenomTrace := v1types.ParseDenomTrace(v1Denom) +func ExtractDenomAndTraceFromV1Denom(v1Denom string) (string, []string) { + v1DenomTrace := types.ParseDenomTrace(v1Denom) // if the path string is empty, then the base denom is the full native denom. if v1DenomTrace.Path == "" { - return v1DenomTrace.BaseDenom, []string{""} + return v1DenomTrace.BaseDenom, nil } splitPath := strings.Split(v1DenomTrace.Path, "/") diff --git a/modules/apps/transfer/internal/convert/convert_test.go b/modules/apps/transfer/internal/convert/convert_test.go index 006bf3eeb32..b406210ad47 100644 --- a/modules/apps/transfer/internal/convert/convert_test.go +++ b/modules/apps/transfer/internal/convert/convert_test.go @@ -7,7 +7,7 @@ import ( errorsmod "cosmossdk.io/errors" - v1types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" ) @@ -19,13 +19,13 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { testCases := []struct { name string - v1Data v1types.FungibleTokenPacketData + v1Data types.FungibleTokenPacketData v3Data v3types.FungibleTokenPacketData expPanic error }{ { "success", - v1types.NewFungibleTokenPacketData("transfer/channel-0/atom", "1000", sender, receiver, ""), + types.NewFungibleTokenPacketData("transfer/channel-0/atom", "1000", sender, receiver, ""), v3types.NewFungibleTokenPacketData( []*v3types.Token{ { @@ -38,20 +38,20 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { }, { "success with empty trace", - v1types.NewFungibleTokenPacketData("atom", "1000", sender, receiver, ""), + types.NewFungibleTokenPacketData("atom", "1000", sender, receiver, ""), v3types.NewFungibleTokenPacketData( []*v3types.Token{ { Denom: "atom", Amount: "1000", - Trace: []string{""}, + Trace: nil, }, }, sender, receiver, ""), nil, }, { "success: base denom with '/'", - v1types.NewFungibleTokenPacketData("transfer/channel-0/atom/withslash", "1000", sender, receiver, ""), + types.NewFungibleTokenPacketData("transfer/channel-0/atom/withslash", "1000", sender, receiver, ""), v3types.NewFungibleTokenPacketData( []*v3types.Token{ { @@ -64,7 +64,7 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { }, { "success: base denom with '/' at the end", - v1types.NewFungibleTokenPacketData("transfer/channel-0/atom/", "1000", sender, receiver, ""), + types.NewFungibleTokenPacketData("transfer/channel-0/atom/", "1000", sender, receiver, ""), v3types.NewFungibleTokenPacketData( []*v3types.Token{ { @@ -77,7 +77,7 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { }, { "success: longer trace base denom with '/'", - v1types.NewFungibleTokenPacketData("transfer/channel-0/transfer/channel-1/atom/pool", "1000", sender, receiver, ""), + types.NewFungibleTokenPacketData("transfer/channel-0/transfer/channel-1/atom/pool", "1000", sender, receiver, ""), v3types.NewFungibleTokenPacketData( []*v3types.Token{ { @@ -90,7 +90,7 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { }, { "success: longer trace with non transfer port", - v1types.NewFungibleTokenPacketData("transfer/channel-0/transfer/channel-1/transfer-custom/channel-2/atom", "1000", sender, receiver, ""), + types.NewFungibleTokenPacketData("transfer/channel-0/transfer/channel-1/transfer-custom/channel-2/atom", "1000", sender, receiver, ""), v3types.NewFungibleTokenPacketData( []*v3types.Token{ { @@ -103,7 +103,7 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { }, { "success: base denom with slash, trace with non transfer port", - v1types.NewFungibleTokenPacketData("transfer/channel-0/transfer/channel-1/transfer-custom/channel-2/atom/pool", "1000", sender, receiver, ""), + types.NewFungibleTokenPacketData("transfer/channel-0/transfer/channel-1/transfer-custom/channel-2/atom/pool", "1000", sender, receiver, ""), v3types.NewFungibleTokenPacketData( []*v3types.Token{ { @@ -116,9 +116,9 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { }, { "failure: panics with empty denom", - v1types.NewFungibleTokenPacketData("", "1000", sender, receiver, ""), + types.NewFungibleTokenPacketData("", "1000", sender, receiver, ""), v3types.FungibleTokenPacketData{}, - errorsmod.Wrap(v1types.ErrInvalidDenomForTransfer, "base denomination cannot be blank"), + errorsmod.Wrap(types.ErrInvalidDenomForTransfer, "base denomination cannot be blank"), }, } @@ -126,11 +126,11 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { expPass := tc.expPanic == nil if expPass { v3Data := PacketDataV1ToV3(tc.v1Data) - require.Equal(t, tc.v3Data, v3Data) + require.Equal(t, tc.v3Data, v3Data, "test case: %s", tc.name) } else { require.PanicsWithError(t, tc.expPanic.Error(), func() { PacketDataV1ToV3(tc.v1Data) - }) + }, "test case: %s", tc.name) } } } diff --git a/modules/apps/transfer/keeper/mbt_relay_test.go b/modules/apps/transfer/keeper/mbt_relay_test.go index e2abbde9f1a..d12abd8a589 100644 --- a/modules/apps/transfer/keeper/mbt_relay_test.go +++ b/modules/apps/transfer/keeper/mbt_relay_test.go @@ -19,7 +19,9 @@ import ( "github.com/cometbft/cometbft/crypto" + convert "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/convert" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" @@ -65,7 +67,7 @@ type FungibleTokenPacket struct { SourcePort string DestChannel string DestPort string - Data types.FungibleTokenPacketData + Data v3types.FungibleTokenPacketData } type OnRecvPacketTestCase = struct { @@ -143,17 +145,24 @@ func BalancesFromTla(tla []TlaBalance) []Balance { } func FungibleTokenPacketFromTla(packet TlaFungibleTokenPacket) FungibleTokenPacket { + denom, trace := convert.ExtractDenomAndTraceFromV1Denom(DenomFromTla(packet.Data.Denom)) return FungibleTokenPacket{ SourceChannel: packet.SourceChannel, SourcePort: packet.SourcePort, DestChannel: packet.DestChannel, DestPort: packet.DestPort, - Data: types.NewFungibleTokenPacketData( - DenomFromTla(packet.Data.Denom), - packet.Data.Amount, + Data: v3types.NewFungibleTokenPacketData( + []*v3types.Token{ + { + Denom: denom, + Amount: packet.Data.Amount, + Trace: trace, + }, + }, AddressFromString(packet.Data.Sender), AddressFromString(packet.Data.Receiver), - ""), + "", + ), } } @@ -310,7 +319,7 @@ func (suite *KeeperTestSuite) TestModelBasedRelay() { for i, tlaTc := range tlaTestCases { tc := OnRecvPacketTestCaseFromTla(tlaTc) registerDenomFn := func() { - denomTrace := types.ParseDenomTrace(tc.packet.Data.Denom) + denomTrace := types.ParseDenomTrace(tc.packet.Data.Tokens[0].GetFullDenomPath()) traceHash := denomTrace.Hash() if !suite.chainB.GetSimApp().TransferKeeper.HasDenomTrace(suite.chainB.GetContext(), traceHash) { suite.chainB.GetSimApp().TransferKeeper.SetDenomTrace(suite.chainB.GetContext(), denomTrace) @@ -337,11 +346,11 @@ func (suite *KeeperTestSuite) TestModelBasedRelay() { panic(errors.New("MBT failed to convert sender address")) } registerDenomFn() - denomTrace := types.ParseDenomTrace(tc.packet.Data.Denom) + denomTrace := types.ParseDenomTrace(tc.packet.Data.Tokens[0].GetFullDenomPath()) denom := denomTrace.IBCDenom() err = sdk.ValidateDenom(denom) if err == nil { - amount, ok := sdkmath.NewIntFromString(tc.packet.Data.Amount) + amount, ok := sdkmath.NewIntFromString(tc.packet.Data.Tokens[0].Amount) if !ok { panic(errors.New("MBT failed to parse amount from string")) } diff --git a/modules/apps/transfer/keeper/msg_server.go b/modules/apps/transfer/keeper/msg_server.go index 94536cba937..aa81a9a228a 100644 --- a/modules/apps/transfer/keeper/msg_server.go +++ b/modules/apps/transfer/keeper/msg_server.go @@ -26,11 +26,24 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types. return nil, err } - // TODO: replace with correct usage. - token := msg.GetTokens()[0] + // tokens will always be an array, but may contain a single element + // if the ics20-1 token field is populated, and the ics20-2 array is empty. + tokens := msg.GetTokens() - if !k.bankKeeper.IsSendEnabledCoin(ctx, token) { - return nil, errorsmod.Wrapf(types.ErrSendDisabled, "%s transfers are currently disabled", token.Denom) + appVersion, found := k.ics4Wrapper.GetAppVersion(ctx, msg.SourcePort, msg.SourceChannel) + if !found { + return nil, errorsmod.Wrapf(ibcerrors.ErrInvalidRequest, "application version not found for source port: %s and source channel: %s", msg.SourcePort, msg.SourceChannel) + } + + // ics20-1 only supports a single token, so if that is the current version, we must only process a single token. + if appVersion == types.Version1 && len(tokens) > 1 { + return nil, errorsmod.Wrapf(ibcerrors.ErrInvalidRequest, "cannot transfer multiple tokens with ics20-1") + } + + for _, token := range tokens { + if !k.bankKeeper.IsSendEnabledCoin(ctx, token) { + return nil, errorsmod.Wrapf(types.ErrSendDisabled, "transfers are currently disabled for %s", token.Denom) + } } if k.bankKeeper.BlockedAddr(sender) { @@ -38,28 +51,29 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types. } sequence, err := k.sendTransfer( - ctx, msg.SourcePort, msg.SourceChannel, token, sender, msg.Receiver, msg.TimeoutHeight, msg.TimeoutTimestamp, + ctx, msg.SourcePort, msg.SourceChannel, tokens, sender, msg.Receiver, msg.TimeoutHeight, msg.TimeoutTimestamp, msg.Memo) if err != nil { return nil, err } - k.Logger(ctx).Info("IBC fungible token transfer", "token", token.Denom, "amount", token.Amount.String(), "sender", msg.Sender, "receiver", msg.Receiver) - - ctx.EventManager().EmitEvents(sdk.Events{ - sdk.NewEvent( + events := make([]sdk.Event, 0, len(tokens)+1) + for _, token := range tokens { + k.Logger(ctx).Info("IBC fungible token transfer", "token", token.Denom, "amount", token.Amount.String(), "sender", msg.Sender, "receiver", msg.Receiver) + events = append(events, sdk.NewEvent( types.EventTypeTransfer, - sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender), + sdk.NewAttribute(types.AttributeKeySender, msg.Sender), sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver), - sdk.NewAttribute(types.AttributeKeyAmount, token.Amount.String()), sdk.NewAttribute(types.AttributeKeyDenom, token.Denom), + sdk.NewAttribute(types.AttributeKeyAmount, token.Amount.String()), sdk.NewAttribute(types.AttributeKeyMemo, msg.Memo), - ), - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), - ), - }) + )) + } + events = append(events, sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + )) + ctx.EventManager().EmitEvents(events) return &types.MsgTransferResponse{Sequence: sequence}, nil } diff --git a/modules/apps/transfer/keeper/msg_server_test.go b/modules/apps/transfer/keeper/msg_server_test.go index 957ccf82c94..355b21e5b88 100644 --- a/modules/apps/transfer/keeper/msg_server_test.go +++ b/modules/apps/transfer/keeper/msg_server_test.go @@ -1,27 +1,62 @@ package keeper_test import ( + "errors" + "strings" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" ibctesting "github.com/cosmos/ibc-go/v8/testing" ) // TestMsgTransfer tests Transfer rpc handler func (suite *KeeperTestSuite) TestMsgTransfer() { var msg *types.MsgTransfer + var path *ibctesting.Path + var coin1 sdk.Coin + var coin2 sdk.Coin testCases := []struct { - name string - malleate func() - expPass bool + name string + malleate func() + expError error + multiDenom bool }{ { - "success", + "success: single denom", func() {}, + nil, + false, + }, + { + "success: multi-denom with version ics20-2", + func() { + coin2 = sdk.NewCoin("bond", sdkmath.NewInt(100)) + coins := sdk.NewCoins(coin1, coin2) + + // send some coins of the second denom from bank module to the sender account as well + suite.Require().NoError(suite.chainA.GetSimApp().BankKeeper.MintCoins(suite.chainA.GetContext(), types.ModuleName, sdk.NewCoins(coin2))) + suite.Require().NoError(suite.chainA.GetSimApp().BankKeeper.SendCoinsFromModuleToAccount(suite.chainA.GetContext(), types.ModuleName, suite.chainA.SenderAccount.GetAddress(), sdk.NewCoins(coin2))) + + msg = types.NewMsgTransfer( + path.EndpointA.ChannelConfig.PortID, + path.EndpointA.ChannelID, + coins, + suite.chainA.SenderAccount.GetAddress().String(), + suite.chainB.SenderAccount.GetAddress().String(), + suite.chainB.GetTimeoutHeight(), 0, // only use timeout height + "memo", + ) + }, + nil, true, }, { @@ -34,10 +69,11 @@ func (suite *KeeperTestSuite) TestMsgTransfer() { ) suite.Require().NoError(err) }, - true, + nil, + false, }, { - "send transfers disabled", + "failure: send transfers disabled", func() { suite.chainA.GetSimApp().TransferKeeper.SetParams(suite.chainA.GetContext(), types.Params{ @@ -45,24 +81,27 @@ func (suite *KeeperTestSuite) TestMsgTransfer() { }, ) }, + types.ErrSendDisabled, false, }, { - "invalid sender", + "failure: invalid sender", func() { msg.Sender = "address" }, + errors.New("decoding bech32 failed"), false, }, { - "sender is a blocked address", + "failure: sender is a blocked address", func() { msg.Sender = suite.chainA.GetSimApp().AccountKeeper.GetModuleAddress(types.ModuleName).String() }, + ibcerrors.ErrUnauthorized, false, }, { - "bank send disabled for denom", + "failure: bank send disabled for denom", func() { err := suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), banktypes.Params{ @@ -71,15 +110,45 @@ func (suite *KeeperTestSuite) TestMsgTransfer() { ) suite.Require().NoError(err) }, + types.ErrSendDisabled, false, }, { - "channel does not exist", + "failure: channel does not exist", func() { msg.SourceChannel = "channel-100" }, + ibcerrors.ErrInvalidRequest, false, }, + { + "failure: multidenom with ics20-1", + func() { + coin2 = sdk.NewCoin("bond", sdkmath.NewInt(100)) + coins := sdk.NewCoins(coin1, coin2) + + // send some coins of the second denom from bank module to the sender account as well + suite.Require().NoError(suite.chainA.GetSimApp().BankKeeper.MintCoins(suite.chainA.GetContext(), types.ModuleName, sdk.NewCoins(coin2))) + suite.Require().NoError(suite.chainA.GetSimApp().BankKeeper.SendCoinsFromModuleToAccount(suite.chainA.GetContext(), types.ModuleName, suite.chainA.SenderAccount.GetAddress(), sdk.NewCoins(coin2))) + + msg = types.NewMsgTransfer( + path.EndpointA.ChannelConfig.PortID, + path.EndpointA.ChannelID, + coins, + suite.chainA.SenderAccount.GetAddress().String(), + suite.chainB.SenderAccount.GetAddress().String(), + suite.chainB.GetTimeoutHeight(), 0, // only use timeout height + "memo", + ) + + // explicitly set to ics20-1 which does not support multi-denom + path.EndpointA.UpdateChannel(func(channel *channeltypes.Channel) { + channel.Version = types.Version1 + }) + }, + ibcerrors.ErrInvalidRequest, + true, + }, } for _, tc := range testCases { @@ -88,14 +157,16 @@ func (suite *KeeperTestSuite) TestMsgTransfer() { suite.Run(tc.name, func() { suite.SetupTest() - path := ibctesting.NewTransferPath(suite.chainA, suite.chainB) + path = ibctesting.NewTransferPath(suite.chainA, suite.chainB) path.Setup() - coin := sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100)) + coin1 = sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100)) msg = types.NewMsgTransfer( path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, - sdk.NewCoins(coin), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), + sdk.NewCoins(coin1), + suite.chainA.SenderAccount.GetAddress().String(), + suite.chainB.SenderAccount.GetAddress().String(), suite.chainB.GetTimeoutHeight(), 0, // only use timeout height "memo", ) @@ -107,24 +178,55 @@ func (suite *KeeperTestSuite) TestMsgTransfer() { // Verify events events := ctx.EventManager().Events().ToABCIEvents() - 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", - }, + + var expEvents []abci.Event + if tc.multiDenom { + expEvents = sdk.Events{ + sdk.NewEvent(types.EventTypeTransfer, + sdk.NewAttribute(types.AttributeKeySender, msg.Sender), + sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver), + sdk.NewAttribute(types.AttributeKeyMemo, msg.Memo), + sdk.NewAttribute(types.AttributeKeyDenom, coin1.Denom), + sdk.NewAttribute(types.AttributeKeyAmount, coin1.Amount.String()), + ), + sdk.NewEvent(types.EventTypeTransfer, + sdk.NewAttribute(types.AttributeKeySender, msg.Sender), + sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver), + sdk.NewAttribute(types.AttributeKeyMemo, msg.Memo), + sdk.NewAttribute(types.AttributeKeyDenom, coin2.Denom), + sdk.NewAttribute(types.AttributeKeyAmount, coin2.Amount.String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + ), + }.ToABCIEvents() + } else { + expEvents = sdk.Events{ + sdk.NewEvent(types.EventTypeTransfer, + sdk.NewAttribute(types.AttributeKeySender, msg.Sender), + sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver), + sdk.NewAttribute(types.AttributeKeyMemo, msg.Memo), + sdk.NewAttribute(types.AttributeKeyDenom, coin1.Denom), + sdk.NewAttribute(types.AttributeKeyAmount, coin1.Amount.String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + ), + }.ToABCIEvents() } - if tc.expPass { + expPass := tc.expError == nil + if expPass { suite.Require().NoError(err) suite.Require().NotNil(res) suite.Require().NotEqual(res.Sequence, uint64(0)) - ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) + ibctesting.AssertEvents(&suite.Suite, expEvents, events) } else { - suite.Require().Error(err) suite.Require().Nil(res) + suite.Require().Error(err) + suite.Require().True(errors.Is(err, tc.expError) || strings.Contains(err.Error(), tc.expError.Error()), err.Error()) suite.Require().Len(events, 0) } }) diff --git a/modules/apps/transfer/keeper/relay.go b/modules/apps/transfer/keeper/relay.go index 3bae0b9da03..0671d64fffd 100644 --- a/modules/apps/transfer/keeper/relay.go +++ b/modules/apps/transfer/keeper/relay.go @@ -12,7 +12,9 @@ import ( "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/convert" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v8/modules/core/24-host" @@ -56,7 +58,7 @@ func (k Keeper) sendTransfer( ctx sdk.Context, sourcePort, sourceChannel string, - token sdk.Coin, + coins sdk.Coins, sender sdk.AccAddress, receiver string, timeoutHeight clienttypes.Height, @@ -71,6 +73,11 @@ func (k Keeper) sendTransfer( destinationPort := channel.Counterparty.PortId destinationChannel := channel.Counterparty.ChannelId + labels := []metrics.Label{ + telemetry.NewLabel(coretypes.LabelDestinationPort, destinationPort), + telemetry.NewLabel(coretypes.LabelDestinationChannel, destinationChannel), + } + // begin createOutgoingPacket logic // See spec for this logic: https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#packet-relay channelCap, ok := k.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(sourcePort, sourceChannel)) @@ -78,61 +85,66 @@ func (k Keeper) sendTransfer( return 0, errorsmod.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability") } - // NOTE: denomination and hex hash correctness checked during msg.ValidateBasic - fullDenomPath := token.Denom + var tokens []*v3types.Token - var err error + for _, coin := range coins { + // NOTE: denomination and hex hash correctness checked during msg.ValidateBasic + fullDenomPath := coin.Denom - // deconstruct the token denomination into the denomination trace info - // to determine if the sender is the source chain - if strings.HasPrefix(token.Denom, "ibc/") { - fullDenomPath, err = k.DenomPathFromHash(ctx, token.Denom) - if err != nil { - return 0, err + var err error + + // deconstruct the token denomination into the denomination trace info + // to determine if the sender is the source chain + if strings.HasPrefix(coin.Denom, "ibc/") { + fullDenomPath, err = k.DenomPathFromHash(ctx, coin.Denom) + if err != nil { + return 0, err + } } - } - labels := []metrics.Label{ - telemetry.NewLabel(coretypes.LabelDestinationPort, destinationPort), - telemetry.NewLabel(coretypes.LabelDestinationChannel, destinationChannel), - } + // NOTE: SendTransfer simply sends the denomination as it exists on its own + // chain inside the packet data. The receiving chain will perform denom + // prefixing as necessary. - // NOTE: SendTransfer simply sends the denomination as it exists on its own - // chain inside the packet data. The receiving chain will perform denom - // prefixing as necessary. + if types.SenderChainIsSource(sourcePort, sourceChannel, fullDenomPath) { + labels = append(labels, telemetry.NewLabel(coretypes.LabelSource, "true")) - if types.SenderChainIsSource(sourcePort, sourceChannel, fullDenomPath) { - labels = append(labels, telemetry.NewLabel(coretypes.LabelSource, "true")) + // obtain the escrow address for the source channel end + escrowAddress := types.GetEscrowAddress(sourcePort, sourceChannel) + if err := k.escrowToken(ctx, sender, escrowAddress, coin); err != nil { + return 0, err + } - // obtain the escrow address for the source channel end - escrowAddress := types.GetEscrowAddress(sourcePort, sourceChannel) - if err := k.escrowToken(ctx, sender, escrowAddress, token); err != nil { - return 0, err - } + } else { + labels = append(labels, telemetry.NewLabel(coretypes.LabelSource, "false")) - } else { - labels = append(labels, telemetry.NewLabel(coretypes.LabelSource, "false")) + // transfer the coins to the module account and burn them + if err := k.bankKeeper.SendCoinsFromAccountToModule( + ctx, sender, types.ModuleName, sdk.NewCoins(coin), + ); err != nil { + return 0, err + } - // transfer the coins to the module account and burn them - if err := k.bankKeeper.SendCoinsFromAccountToModule( - ctx, sender, types.ModuleName, sdk.NewCoins(token), - ); err != nil { - return 0, err + if err := k.bankKeeper.BurnCoins( + ctx, types.ModuleName, sdk.NewCoins(coin), + ); err != nil { + // NOTE: should not happen as the module account was + // retrieved on the step above and it has enough balance + // to burn. + panic(fmt.Errorf("cannot burn coins after a successful send to a module account: %v", err)) + } } - if err := k.bankKeeper.BurnCoins( - ctx, types.ModuleName, sdk.NewCoins(token), - ); err != nil { - // NOTE: should not happen as the module account was - // retrieved on the step above and it has enough balance - // to burn. - panic(fmt.Errorf("cannot burn coins after a successful send to a module account: %v", err)) + denom, trace := convert.ExtractDenomAndTraceFromV1Denom(fullDenomPath) + token := &v3types.Token{ + Denom: denom, + Amount: coin.Amount.String(), + Trace: trace, } + tokens = append(tokens, token) } - packetData := types.NewFungibleTokenPacketData( - fullDenomPath, token.Amount.String(), sender.String(), receiver, memo, - ) + packetData := v3types.NewFungibleTokenPacketData(tokens, sender.String(), receiver, memo) sequence, err := k.ics4Wrapper.SendPacket(ctx, channelCap, sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, packetData.GetBytes()) if err != nil { @@ -140,12 +152,15 @@ func (k Keeper) sendTransfer( } defer func() { - if token.Amount.IsInt64() { - telemetry.SetGaugeWithLabels( - []string{"tx", "msg", "ibc", "transfer"}, - float32(token.Amount.Int64()), - []metrics.Label{telemetry.NewLabel(coretypes.LabelDenom, fullDenomPath)}, - ) + for _, token := range tokens { + amount, ok := sdkmath.NewIntFromString(token.Amount) + if ok && amount.IsInt64() { + telemetry.SetGaugeWithLabels( + []string{"tx", "msg", "ibc", "transfer"}, + float32(amount.Int64()), + []metrics.Label{telemetry.NewLabel(coretypes.LabelDenom, token.GetFullDenomPath())}, + ) + } } telemetry.IncrCounterWithLabels( @@ -163,7 +178,7 @@ func (k Keeper) sendTransfer( // and sent to the receiving address. Otherwise if the sender chain is sending // back tokens this chain originally transferred to it, the tokens are // unescrowed and sent to the receiving address. -func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketData) error { +func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data v3types.FungibleTokenPacketData) error { // validate packet data upon receiving if err := data.ValidateBasic(); err != nil { return errorsmod.Wrapf(err, "error validating ICS-20 transfer packet data") @@ -179,49 +194,114 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data t return errorsmod.Wrapf(err, "failed to decode receiver address: %s", data.Receiver) } - // parse the transfer amount - transferAmount, ok := sdkmath.NewIntFromString(data.Amount) - if !ok { - return errorsmod.Wrapf(types.ErrInvalidAmount, "unable to parse transfer amount: %s", data.Amount) - } + for _, token := range data.Tokens { + fullDenomPath := token.GetFullDenomPath() - labels := []metrics.Label{ - telemetry.NewLabel(coretypes.LabelSourcePort, packet.GetSourcePort()), - telemetry.NewLabel(coretypes.LabelSourceChannel, packet.GetSourceChannel()), - } + labels := []metrics.Label{ + telemetry.NewLabel(coretypes.LabelSourcePort, packet.GetSourcePort()), + telemetry.NewLabel(coretypes.LabelSourceChannel, packet.GetSourceChannel()), + } - // This is the prefix that would have been prefixed to the denomination - // on sender chain IF and only if the token originally came from the - // receiving chain. - // - // NOTE: We use SourcePort and SourceChannel here, because the counterparty - // chain would have prefixed with DestPort and DestChannel when originally - // receiving this coin as seen in the "sender chain is the source" condition. - if types.ReceiverChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), data.Denom) { - // sender chain is not the source, unescrow tokens - - // remove prefix added by sender chain - voucherPrefix := types.GetDenomPrefix(packet.GetSourcePort(), packet.GetSourceChannel()) - unprefixedDenom := data.Denom[len(voucherPrefix):] - - // coin denomination used in sending from the escrow address - denom := unprefixedDenom - - // The denomination used to send the coins is either the native denom or the hash of the path - // if the denomination is not native. - denomTrace := types.ParseDenomTrace(unprefixedDenom) - if !denomTrace.IsNativeDenom() { - denom = denomTrace.IBCDenom() + // parse the transfer amount + transferAmount, ok := sdkmath.NewIntFromString(token.Amount) + if !ok { + return errorsmod.Wrapf(types.ErrInvalidAmount, "unable to parse transfer amount: %s", token.Amount) } - token := sdk.NewCoin(denom, transferAmount) - if k.bankKeeper.BlockedAddr(receiver) { - return errorsmod.Wrapf(ibcerrors.ErrUnauthorized, "%s is not allowed to receive funds", receiver) + // This is the prefix that would have been prefixed to the denomination + // on sender chain IF and only if the token originally came from the + // receiving chain. + // + // NOTE: We use SourcePort and SourceChannel here, because the counterparty + // chain would have prefixed with DestPort and DestChannel when originally + // receiving this coin as seen in the "sender chain is the source" condition. + if types.ReceiverChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), fullDenomPath) { + // sender chain is not the source, unescrow tokens + + // remove prefix added by sender chain + voucherPrefix := types.GetDenomPrefix(packet.GetSourcePort(), packet.GetSourceChannel()) + unprefixedDenom := fullDenomPath[len(voucherPrefix):] + + // coin denomination used in sending from the escrow address + denom := unprefixedDenom + + // The denomination used to send the coins is either the native denom or the hash of the path + // if the denomination is not native. + denomTrace := types.ParseDenomTrace(unprefixedDenom) + if !denomTrace.IsNativeDenom() { + denom = denomTrace.IBCDenom() + } + token := sdk.NewCoin(denom, transferAmount) + + if k.bankKeeper.BlockedAddr(receiver) { + return errorsmod.Wrapf(ibcerrors.ErrUnauthorized, "%s is not allowed to receive funds", receiver) + } + + escrowAddress := types.GetEscrowAddress(packet.GetDestPort(), packet.GetDestChannel()) + if err := k.unescrowToken(ctx, escrowAddress, receiver, token); err != nil { + return err + } + + defer func() { + if transferAmount.IsInt64() { + telemetry.SetGaugeWithLabels( + []string{"ibc", types.ModuleName, "packet", "receive"}, + float32(transferAmount.Int64()), + []metrics.Label{telemetry.NewLabel(coretypes.LabelDenom, unprefixedDenom)}, + ) + } + + telemetry.IncrCounterWithLabels( + []string{"ibc", types.ModuleName, "receive"}, + 1, + append( + labels, telemetry.NewLabel(coretypes.LabelSource, "true"), + ), + ) + }() + + return nil } - escrowAddress := types.GetEscrowAddress(packet.GetDestPort(), packet.GetDestChannel()) - if err := k.unescrowToken(ctx, escrowAddress, receiver, token); err != nil { - return err + // sender chain is the source, mint vouchers + + // since SendPacket did not prefix the denomination, we must prefix denomination here + prefixedDenom := types.GetPrefixedDenom(packet.GetDestPort(), packet.GetDestChannel(), fullDenomPath) + + // construct the denomination trace from the full raw denomination + denomTrace := types.ParseDenomTrace(prefixedDenom) + + traceHash := denomTrace.Hash() + if !k.HasDenomTrace(ctx, traceHash) { + k.SetDenomTrace(ctx, denomTrace) + } + + voucherDenom := denomTrace.IBCDenom() + if !k.bankKeeper.HasDenomMetaData(ctx, voucherDenom) { + k.setDenomMetadata(ctx, denomTrace) + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeDenomTrace, + sdk.NewAttribute(types.AttributeKeyTraceHash, traceHash.String()), + sdk.NewAttribute(types.AttributeKeyDenom, voucherDenom), + ), + ) + voucher := sdk.NewCoin(voucherDenom, transferAmount) + + // mint new tokens if the source of the transfer is the same chain + if err := k.bankKeeper.MintCoins( + ctx, types.ModuleName, sdk.NewCoins(voucher), + ); err != nil { + return errorsmod.Wrap(err, "failed to mint IBC tokens") + } + + // send to receiver + if err := k.bankKeeper.SendCoinsFromModuleToAccount( + ctx, types.ModuleName, receiver, sdk.NewCoins(voucher), + ); err != nil { + return errorsmod.Wrapf(err, "failed to send coins to receiver %s", receiver.String()) } defer func() { @@ -229,7 +309,7 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data t telemetry.SetGaugeWithLabels( []string{"ibc", types.ModuleName, "packet", "receive"}, float32(transferAmount.Int64()), - []metrics.Label{telemetry.NewLabel(coretypes.LabelDenom, unprefixedDenom)}, + []metrics.Label{telemetry.NewLabel(coretypes.LabelDenom, fullDenomPath)}, ) } @@ -237,73 +317,12 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data t []string{"ibc", types.ModuleName, "receive"}, 1, append( - labels, telemetry.NewLabel(coretypes.LabelSource, "true"), + labels, telemetry.NewLabel(coretypes.LabelSource, "false"), ), ) }() - - return nil - } - - // sender chain is the source, mint vouchers - - // since SendPacket did not prefix the denomination, we must prefix denomination here - prefixedDenom := types.GetPrefixedDenom(packet.GetDestPort(), packet.GetDestChannel(), data.Denom) - - // construct the denomination trace from the full raw denomination - denomTrace := types.ParseDenomTrace(prefixedDenom) - - traceHash := denomTrace.Hash() - if !k.HasDenomTrace(ctx, traceHash) { - k.SetDenomTrace(ctx, denomTrace) } - voucherDenom := denomTrace.IBCDenom() - if !k.bankKeeper.HasDenomMetaData(ctx, voucherDenom) { - k.setDenomMetadata(ctx, denomTrace) - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeDenomTrace, - sdk.NewAttribute(types.AttributeKeyTraceHash, traceHash.String()), - sdk.NewAttribute(types.AttributeKeyDenom, voucherDenom), - ), - ) - voucher := sdk.NewCoin(voucherDenom, transferAmount) - - // mint new tokens if the source of the transfer is the same chain - if err := k.bankKeeper.MintCoins( - ctx, types.ModuleName, sdk.NewCoins(voucher), - ); err != nil { - return errorsmod.Wrap(err, "failed to mint IBC tokens") - } - - // send to receiver - if err := k.bankKeeper.SendCoinsFromModuleToAccount( - ctx, types.ModuleName, receiver, sdk.NewCoins(voucher), - ); err != nil { - return errorsmod.Wrapf(err, "failed to send coins to receiver %s", receiver.String()) - } - - defer func() { - if transferAmount.IsInt64() { - telemetry.SetGaugeWithLabels( - []string{"ibc", types.ModuleName, "packet", "receive"}, - float32(transferAmount.Int64()), - []metrics.Label{telemetry.NewLabel(coretypes.LabelDenom, data.Denom)}, - ) - } - - telemetry.IncrCounterWithLabels( - []string{"ibc", types.ModuleName, "receive"}, - 1, - append( - labels, telemetry.NewLabel(coretypes.LabelSource, "false"), - ), - ) - }() - return nil } @@ -311,7 +330,7 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data t // acknowledgement written on the receiving chain. If the acknowledgement // was a success then nothing occurs. If the acknowledgement failed, then // the sender is refunded their tokens using the refundPacketToken function. -func (k Keeper) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketData, ack channeltypes.Acknowledgement) error { +func (k Keeper) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, data v3types.FungibleTokenPacketData, ack channeltypes.Acknowledgement) error { switch ack.Response.(type) { case *channeltypes.Acknowledgement_Result: // the acknowledgement succeeded on the receiving chain so nothing @@ -326,7 +345,7 @@ func (k Keeper) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Pac // OnTimeoutPacket refunds the sender since the original packet sent was // never received and has been timed out. -func (k Keeper) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketData) error { +func (k Keeper) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, data v3types.FungibleTokenPacketData) error { return k.refundPacketToken(ctx, packet, data) } @@ -334,40 +353,44 @@ func (k Keeper) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, dat // if the sending chain was the source chain. Otherwise, the sent tokens // were burnt in the original send so new tokens are minted and sent to // the sending address. -func (k Keeper) refundPacketToken(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketData) error { +func (k Keeper) refundPacketToken(ctx sdk.Context, packet channeltypes.Packet, data v3types.FungibleTokenPacketData) error { // NOTE: packet data type already checked in handler.go - // parse the denomination from the full denom path - trace := types.ParseDenomTrace(data.Denom) + for _, token := range data.Tokens { + fullDenomPath := token.GetFullDenomPath() - // parse the transfer amount - transferAmount, ok := sdkmath.NewIntFromString(data.Amount) - if !ok { - return errorsmod.Wrapf(types.ErrInvalidAmount, "unable to parse transfer amount (%s) into math.Int", data.Amount) - } - token := sdk.NewCoin(trace.IBCDenom(), transferAmount) + // parse the denomination from the full denom path + trace := types.ParseDenomTrace(fullDenomPath) - // decode the sender address - sender, err := sdk.AccAddressFromBech32(data.Sender) - if err != nil { - return err - } + // parse the transfer amount + transferAmount, ok := sdkmath.NewIntFromString(token.Amount) + if !ok { + return errorsmod.Wrapf(types.ErrInvalidAmount, "unable to parse transfer amount (%s) into math.Int", transferAmount) + } + token := sdk.NewCoin(trace.IBCDenom(), transferAmount) - if types.SenderChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), data.Denom) { - // unescrow tokens back to sender - escrowAddress := types.GetEscrowAddress(packet.GetSourcePort(), packet.GetSourceChannel()) - return k.unescrowToken(ctx, escrowAddress, sender, token) - } + // decode the sender address + sender, err := sdk.AccAddressFromBech32(data.Sender) + if err != nil { + return err + } - // mint vouchers back to sender - if err := k.bankKeeper.MintCoins( - ctx, types.ModuleName, sdk.NewCoins(token), - ); err != nil { - return err - } + if types.SenderChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), fullDenomPath) { + // unescrow tokens back to sender + escrowAddress := types.GetEscrowAddress(packet.GetSourcePort(), packet.GetSourceChannel()) + return k.unescrowToken(ctx, escrowAddress, sender, token) + } - if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sender, sdk.NewCoins(token)); err != nil { - panic(fmt.Errorf("unable to send coins from module to account despite previously minting coins to module account: %v", err)) + // mint vouchers back to sender + if err := k.bankKeeper.MintCoins( + ctx, types.ModuleName, sdk.NewCoins(token), + ); err != nil { + return err + } + + if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sender, sdk.NewCoins(token)); err != nil { + panic(fmt.Errorf("unable to send coins from module to account despite previously minting coins to module account: %v", err)) + } } return nil diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index 24ab6f9eb69..e4ce18817fb 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -10,7 +10,9 @@ import ( banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/convert" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" ibctesting "github.com/cosmos/ibc-go/v8/testing" @@ -247,10 +249,10 @@ func (suite *KeeperTestSuite) TestSendTransferSetsTotalEscrowAmountForSourceIBCT // malleate function allows for testing invalid cases. func (suite *KeeperTestSuite) TestOnRecvPacket() { var ( - trace types.DenomTrace amount sdkmath.Int receiver string memo string + denomTrace types.DenomTrace expEscrowAmount sdkmath.Int // total amount in escrow for denom on receiving chain ) @@ -290,7 +292,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { { "empty coin", func() { - trace = types.DenomTrace{} + denomTrace = types.DenomTrace{} amount = sdkmath.ZeroInt() expEscrowAmount = sdkmath.NewInt(100) }, true, false, @@ -396,20 +398,28 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { seq++ // NOTE: trace must be explicitly changed in malleate to test invalid cases - trace = types.ParseDenomTrace(types.GetPrefixedDenom(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.DefaultBondDenom)) + denomTrace = types.ParseDenomTrace(types.GetPrefixedDenom(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.DefaultBondDenom)) } else { - trace = types.ParseDenomTrace(sdk.DefaultBondDenom) + denomTrace = types.ParseDenomTrace(sdk.DefaultBondDenom) } // send coin from chainA to chainB - coin := sdk.NewCoin(trace.IBCDenom(), amount) + coin := sdk.NewCoin(denomTrace.IBCDenom(), amount) transferMsg := types.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.NewCoins(coin), suite.chainA.SenderAccount.GetAddress().String(), receiver, clienttypes.NewHeight(1, 110), 0, memo) _, err := suite.chainA.SendMsgs(transferMsg) suite.Require().NoError(err) // message committed tc.malleate() - data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), suite.chainA.SenderAccount.GetAddress().String(), receiver, memo) + denom, trace := convert.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) + data := v3types.NewFungibleTokenPacketData( + []*v3types.Token{ + { + Denom: denom, + Amount: amount.String(), + Trace: trace, + }, + }, suite.chainA.SenderAccount.GetAddress().String(), receiver, memo) packet := channeltypes.NewPacket(data.GetBytes(), seq, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) err = suite.chainB.GetSimApp().TransferKeeper.OnRecvPacket(suite.chainB.GetContext(), packet, data) @@ -483,12 +493,16 @@ func (suite *KeeperTestSuite) TestOnRecvPacketSetsTotalEscrowAmountForSourceIBCT BaseDenom: sdk.DefaultBondDenom, Path: fmt.Sprintf("%s/%s/%s/%s", path2.EndpointA.ChannelConfig.PortID, path2.EndpointA.ChannelID, path1.EndpointB.ChannelConfig.PortID, path1.EndpointB.ChannelID), } - data := types.NewFungibleTokenPacketData( - denomTrace.GetFullDenomPath(), - amount.String(), - suite.chainA.SenderAccount.GetAddress().String(), - suite.chainB.SenderAccount.GetAddress().String(), "", - ) + + denom, trace := convert.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) + data := v3types.NewFungibleTokenPacketData( + []*v3types.Token{ + { + Denom: denom, + Amount: amount.String(), + Trace: trace, + }, + }, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), "") packet := channeltypes.NewPacket( data.GetBytes(), seq, @@ -537,7 +551,7 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { var ( successAck = channeltypes.NewResultAcknowledgement([]byte{byte(1)}) failedAck = channeltypes.NewErrorAcknowledgement(fmt.Errorf("failed packet transfer")) - trace types.DenomTrace + denomTrace types.DenomTrace amount sdkmath.Int path *ibctesting.Path expEscrowAmount sdkmath.Int @@ -554,7 +568,7 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { "success ack causes no-op", successAck, func() { - trace = types.ParseDenomTrace(types.GetPrefixedDenom(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, sdk.DefaultBondDenom)) + denomTrace = types.ParseDenomTrace(types.GetPrefixedDenom(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, sdk.DefaultBondDenom)) }, true, true, }, { @@ -562,7 +576,7 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { failedAck, func() { escrow := types.GetEscrowAddress(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - trace = types.ParseDenomTrace(sdk.DefaultBondDenom) + denomTrace = types.ParseDenomTrace(sdk.DefaultBondDenom) coin := sdk.NewCoin(sdk.DefaultBondDenom, amount) suite.Require().NoError(banktestutil.FundAccount(suite.chainA.GetContext(), suite.chainA.GetSimApp().BankKeeper, escrow, sdk.NewCoins(coin))) @@ -575,7 +589,7 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { "unsuccessful refund from source", failedAck, func() { - trace = types.ParseDenomTrace(sdk.DefaultBondDenom) + denomTrace = types.ParseDenomTrace(sdk.DefaultBondDenom) // set escrow amount that would have been stored after successful execution of MsgTransfer suite.chainA.GetSimApp().TransferKeeper.SetTotalEscrowForDenom(suite.chainA.GetContext(), sdk.NewCoin(sdk.DefaultBondDenom, amount)) @@ -587,8 +601,8 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { failedAck, func() { escrow := types.GetEscrowAddress(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - trace = types.ParseDenomTrace(types.GetPrefixedDenom(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.DefaultBondDenom)) - coin := sdk.NewCoin(trace.IBCDenom(), amount) + denomTrace = types.ParseDenomTrace(types.GetPrefixedDenom(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.DefaultBondDenom)) + coin := sdk.NewCoin(denomTrace.IBCDenom(), amount) suite.Require().NoError(banktestutil.FundAccount(suite.chainA.GetContext(), suite.chainA.GetSimApp().BankKeeper, escrow, sdk.NewCoins(coin))) }, false, true, @@ -607,19 +621,27 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { tc.malleate() - data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), "") + denom, trace := convert.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) + data := v3types.NewFungibleTokenPacketData( + []*v3types.Token{ + { + Denom: denom, + Amount: amount.String(), + Trace: trace, + }, + }, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), "") packet := channeltypes.NewPacket(data.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) - preCoin := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), trace.IBCDenom()) + preCoin := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), denomTrace.IBCDenom()) err := suite.chainA.GetSimApp().TransferKeeper.OnAcknowledgementPacket(suite.chainA.GetContext(), packet, data, tc.ack) // check total amount in escrow of sent token denom on sending chain - totalEscrow := suite.chainA.GetSimApp().TransferKeeper.GetTotalEscrowForDenom(suite.chainA.GetContext(), trace.IBCDenom()) + totalEscrow := suite.chainA.GetSimApp().TransferKeeper.GetTotalEscrowForDenom(suite.chainA.GetContext(), denomTrace.IBCDenom()) suite.Require().Equal(expEscrowAmount, totalEscrow.Amount) if tc.expPass { suite.Require().NoError(err) - postCoin := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), trace.IBCDenom()) + postCoin := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), denomTrace.IBCDenom()) deltaAmount := postCoin.Amount.Sub(preCoin.Amount) if tc.success { @@ -692,11 +714,18 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacketSetsTotalEscrowAmountFo ), ) - data := types.NewFungibleTokenPacketData( - denomTrace.GetFullDenomPath(), - amount.String(), + denom, trace := convert.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) + data := v3types.NewFungibleTokenPacketData( + []*v3types.Token{ + { + Denom: denom, + Amount: amount.String(), + Trace: trace, + }, + }, suite.chainB.SenderAccount.GetAddress().String(), - suite.chainA.SenderAccount.GetAddress().String(), "", + suite.chainA.SenderAccount.GetAddress().String(), + "", ) packet := channeltypes.NewPacket( data.GetBytes(), @@ -726,10 +755,10 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacketSetsTotalEscrowAmountFo // so the refunds are occurring on chainA. func (suite *KeeperTestSuite) TestOnTimeoutPacket() { var ( - trace types.DenomTrace path *ibctesting.Path amount sdkmath.Int sender string + denomTrace types.DenomTrace expEscrowAmount sdkmath.Int ) @@ -742,8 +771,8 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() { "successful timeout from sender as source chain", func() { escrow := types.GetEscrowAddress(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - trace = types.ParseDenomTrace(sdk.DefaultBondDenom) - coin := sdk.NewCoin(trace.IBCDenom(), amount) + denomTrace = types.ParseDenomTrace(sdk.DefaultBondDenom) + coin := sdk.NewCoin(denomTrace.IBCDenom(), amount) expEscrowAmount = sdkmath.ZeroInt() // funds the escrow account to have balance @@ -756,8 +785,8 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() { "successful timeout from external chain", func() { escrow := types.GetEscrowAddress(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - trace = types.ParseDenomTrace(types.GetPrefixedDenom(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.DefaultBondDenom)) - coin := sdk.NewCoin(trace.IBCDenom(), amount) + denomTrace = types.ParseDenomTrace(types.GetPrefixedDenom(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.DefaultBondDenom)) + coin := sdk.NewCoin(denomTrace.IBCDenom(), amount) expEscrowAmount = sdkmath.ZeroInt() // funds the escrow account to have balance @@ -767,27 +796,27 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() { { "no balance for coin denom", func() { - trace = types.ParseDenomTrace("bitcoin") + denomTrace = types.ParseDenomTrace("bitcoin") expEscrowAmount = amount // set escrow amount that would have been stored after successful execution of MsgTransfer - suite.chainA.GetSimApp().TransferKeeper.SetTotalEscrowForDenom(suite.chainA.GetContext(), sdk.NewCoin(trace.IBCDenom(), amount)) + suite.chainA.GetSimApp().TransferKeeper.SetTotalEscrowForDenom(suite.chainA.GetContext(), sdk.NewCoin(denomTrace.IBCDenom(), amount)) }, false, }, { "unescrow failed", func() { - trace = types.ParseDenomTrace(sdk.DefaultBondDenom) + denomTrace = types.ParseDenomTrace(sdk.DefaultBondDenom) expEscrowAmount = amount // set escrow amount that would have been stored after successful execution of MsgTransfer - suite.chainA.GetSimApp().TransferKeeper.SetTotalEscrowForDenom(suite.chainA.GetContext(), sdk.NewCoin(trace.IBCDenom(), amount)) + suite.chainA.GetSimApp().TransferKeeper.SetTotalEscrowForDenom(suite.chainA.GetContext(), sdk.NewCoin(denomTrace.IBCDenom(), amount)) }, false, }, { "mint failed", func() { - trace = types.ParseDenomTrace(types.GetPrefixedDenom(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.DefaultBondDenom)) + denomTrace = types.ParseDenomTrace(types.GetPrefixedDenom(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.DefaultBondDenom)) amount = sdkmath.OneInt() sender = "invalid address" }, false, @@ -808,17 +837,25 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() { tc.malleate() - data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), sender, suite.chainB.SenderAccount.GetAddress().String(), "") + denom, trace := convert.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) + data := v3types.NewFungibleTokenPacketData( + []*v3types.Token{ + { + Denom: denom, + Amount: amount.String(), + Trace: trace, + }, + }, sender, suite.chainB.SenderAccount.GetAddress().String(), "") packet := channeltypes.NewPacket(data.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) - preCoin := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), trace.IBCDenom()) + preCoin := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), denomTrace.IBCDenom()) err := suite.chainA.GetSimApp().TransferKeeper.OnTimeoutPacket(suite.chainA.GetContext(), packet, data) - postCoin := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), trace.IBCDenom()) + postCoin := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), denomTrace.IBCDenom()) deltaAmount := postCoin.Amount.Sub(preCoin.Amount) // check total amount in escrow of sent token denom on sending chain - totalEscrow := suite.chainA.GetSimApp().TransferKeeper.GetTotalEscrowForDenom(suite.chainA.GetContext(), trace.IBCDenom()) + totalEscrow := suite.chainA.GetSimApp().TransferKeeper.GetTotalEscrowForDenom(suite.chainA.GetContext(), denomTrace.IBCDenom()) suite.Require().Equal(expEscrowAmount, totalEscrow.Amount) if tc.expPass { @@ -886,12 +923,15 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacketSetsTotalEscrowAmountForSourceI ), ) - data := types.NewFungibleTokenPacketData( - denomTrace.GetFullDenomPath(), - amount.String(), - suite.chainB.SenderAccount.GetAddress().String(), - suite.chainA.SenderAccount.GetAddress().String(), "", - ) + denom, trace := convert.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) + data := v3types.NewFungibleTokenPacketData( + []*v3types.Token{ + { + Denom: denom, + Amount: amount.String(), + Trace: trace, + }, + }, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), "") packet := channeltypes.NewPacket( data.GetBytes(), seq, diff --git a/modules/apps/transfer/types/events.go b/modules/apps/transfer/types/events.go index 89964a8a9a4..20165a5e576 100644 --- a/modules/apps/transfer/types/events.go +++ b/modules/apps/transfer/types/events.go @@ -8,6 +8,7 @@ const ( EventTypeChannelClose = "channel_closed" EventTypeDenomTrace = "denomination_trace" + AttributeKeySender = "sender" AttributeKeyReceiver = "receiver" AttributeKeyDenom = "denom" AttributeKeyAmount = "amount" diff --git a/modules/apps/transfer/types/v3/token.go b/modules/apps/transfer/types/v3/token.go index 21a658723c8..8eea4eab756 100644 --- a/modules/apps/transfer/types/v3/token.go +++ b/modules/apps/transfer/types/v3/token.go @@ -17,6 +17,10 @@ func (t Token) Validate() error { return errorsmod.Wrap(types.ErrInvalidDenomForTransfer, err.Error()) } + if len(t.Trace) == 0 { + return nil + } + trace := strings.Join(t.Trace, "/") identifiers := strings.Split(trace, "/") diff --git a/testing/solomachine.go b/testing/solomachine.go index 34877aa8535..46151bec3ea 100644 --- a/testing/solomachine.go +++ b/testing/solomachine.go @@ -369,21 +369,22 @@ func (solo *Solomachine) ChanCloseConfirm(chain *TestChain, portID, channelID st // SendTransfer constructs a MsgTransfer and sends the message to the given chain. Any number of optional // functions can be provided which will modify the MsgTransfer before SendMsgs is called. func (solo *Solomachine) SendTransfer(chain *TestChain, portID, channelID string, fns ...func(*transfertypes.MsgTransfer)) channeltypes.Packet { - msgTransfer := transfertypes.MsgTransfer{ - SourcePort: portID, - SourceChannel: channelID, - Token: sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100)), - Sender: chain.SenderAccount.GetAddress().String(), - Receiver: chain.SenderAccount.GetAddress().String(), - TimeoutHeight: clienttypes.ZeroHeight(), - TimeoutTimestamp: uint64(chain.GetContext().BlockTime().Add(time.Hour).UnixNano()), - } + msgTransfer := transfertypes.NewMsgTransfer( + portID, + channelID, + sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100))), + chain.SenderAccount.GetAddress().String(), + chain.SenderAccount.GetAddress().String(), + clienttypes.ZeroHeight(), + uint64(chain.GetContext().BlockTime().Add(time.Hour).UnixNano()), + "", + ) for _, fn := range fns { - fn(&msgTransfer) + fn(msgTransfer) } - res, err := chain.SendMsgs(&msgTransfer) + res, err := chain.SendMsgs(msgTransfer) require.NoError(solo.t, err) packet, err := ParsePacketFromEvents(res.Events) From b4244f936eb45e4a2d29a73feb9afc96e59850e5 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Fri, 10 May 2024 16:38:22 +0200 Subject: [PATCH 12/42] multidenom transfer test + ics20-v2 channel upgrade test --- e2e/tests/core/02-client/client_test.go | 2 +- .../core/03-connection/connection_test.go | 5 +- e2e/tests/transfer/authz_test.go | 4 +- e2e/tests/transfer/base_test.go | 129 ++++++++++++++-- e2e/tests/transfer/localhost_test.go | 4 +- e2e/tests/transfer/upgrades_test.go | 146 +++++++++++++++++- e2e/tests/upgrades/genesis_test.go | 4 +- e2e/tests/upgrades/upgrade_test.go | 24 +-- e2e/testsuite/testsuite.go | 6 +- e2e/testsuite/tx.go | 4 +- 10 files changed, 282 insertions(+), 46 deletions(-) diff --git a/e2e/tests/core/02-client/client_test.go b/e2e/tests/core/02-client/client_test.go index 06e1f3f3581..b2276ef64e7 100644 --- a/e2e/tests/core/02-client/client_test.go +++ b/e2e/tests/core/02-client/client_test.go @@ -436,7 +436,7 @@ func (s *ClientTestSuite) TestAllowedClientsParam() { t := s.T() ctx := context.TODO() - _, _ = s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions()) + _, _ = s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) chainA, chainB := s.GetChains() chainAVersion := chainA.Config().Images[0].Version diff --git a/e2e/tests/core/03-connection/connection_test.go b/e2e/tests/core/03-connection/connection_test.go index 6a634986307..b2c08856f38 100644 --- a/e2e/tests/core/03-connection/connection_test.go +++ b/e2e/tests/core/03-connection/connection_test.go @@ -14,6 +14,7 @@ import ( test "github.com/strangelove-ventures/interchaintest/v8/testutil" testifysuite "github.com/stretchr/testify/suite" + sdk "github.com/cosmos/cosmos-sdk/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" paramsproposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal" @@ -62,7 +63,7 @@ func (s *ConnectionTestSuite) TestMaxExpectedTimePerBlockParam() { t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions()) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) chainA, chainB := s.GetChains() chainAVersion := chainA.Config().Images[0].Version @@ -109,7 +110,7 @@ func (s *ConnectionTestSuite) TestMaxExpectedTimePerBlockParam() { t.Run("ensure packets can be received, send from chainB to chainA", func(t *testing.T) { t.Run("send tokens from chainB to chainA", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferAmount(chainBDenom), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") + transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainBDenom)), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") s.AssertTxSuccess(transferTxResp) }) diff --git a/e2e/tests/transfer/authz_test.go b/e2e/tests/transfer/authz_test.go index 33297e0766d..24956d06229 100644 --- a/e2e/tests/transfer/authz_test.go +++ b/e2e/tests/transfer/authz_test.go @@ -34,7 +34,7 @@ func (suite *AuthzTransferTestSuite) TestAuthz_MsgTransfer_Succeeds() { t := suite.T() ctx := context.TODO() - relayer, channelA := suite.SetupChainsRelayerAndChannel(ctx, suite.TransferChannelOptions()) + relayer, channelA := suite.SetupChainsRelayerAndChannel(ctx, suite.TransferChannelOptions(transfertypes.Version)) chainA, chainB := suite.GetChains() chainADenom := chainA.Config().Denom @@ -191,7 +191,7 @@ func (suite *AuthzTransferTestSuite) TestAuthz_InvalidTransferAuthorizations() { t := suite.T() ctx := context.TODO() - relayer, channelA := suite.SetupChainsRelayerAndChannel(ctx, suite.TransferChannelOptions()) + relayer, channelA := suite.SetupChainsRelayerAndChannel(ctx, suite.TransferChannelOptions(transfertypes.Version)) chainA, chainB := suite.GetChains() chainAVersion := chainA.Config().Images[0].Version diff --git a/e2e/tests/transfer/base_test.go b/e2e/tests/transfer/base_test.go index f3272e54a54..d660fe532b9 100644 --- a/e2e/tests/transfer/base_test.go +++ b/e2e/tests/transfer/base_test.go @@ -47,7 +47,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized() { t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions()) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) chainA, chainB := s.GetChains() chainADenom := chainA.Config().Denom @@ -71,7 +71,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized() { chainBVersion := chainB.Config().Images[0].Version t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -114,7 +114,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized() { } t.Run("non-native IBC token transfer from chainB to chainA, receiver is source of tokens", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferAmount(chainBIBCToken.IBCDenom()), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") + transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainBIBCToken.IBCDenom())), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -152,13 +152,114 @@ func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized() { } } +// TestMsgTransfer_Succeeds_MultiDenom will test sending successful IBC transfers from chainA to chainB. +// A multidenom transfer with native chainB tokens and IBC tokens from chainA is executed from chainB to chainA. +func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) + chainA, chainB := s.GetChains() + + chainADenom := chainA.Config().Denom + chainBDenom := chainB.Config().Denom + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) + chainAIBCToken := testsuite.GetIBCToken(chainBDenom, channelA.PortID, channelA.ChannelID) + + t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + s.AssertTxSuccess(transferTxResp) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + 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) + + actualTotalEscrow, err := s.QueryTotalEscrowForDenom(ctx, chainA, chainADenom) + s.Require().NoError(err) + + expectedTotalEscrow := sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.IBCTransferAmount)) + s.Require().Equal(expectedTotalEscrow, actualTotalEscrow) + }) + + 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 := s.QueryBalance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + t.Run("metadata for IBC denomination exists on chainB", func(t *testing.T) { + s.AssertHumanReadableDenom(ctx, chainB, chainADenom, channelA) + }) + + // send the native chainB denom and also the ibc token from chainA + denoms := []string{chainBIBCToken.IBCDenom(), chainBDenom} + var transferCoins []sdk.Coin + for _, denom := range denoms { + transferCoins = append(transferCoins, testvalues.DefaultTransferAmount(denom)) + } + + t.Run("native token from chain B and non-native IBC token from chainA, both to chainA", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, transferCoins, chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") + s.AssertTxSuccess(transferTxResp) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainB, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) + + t.Run("chain A native denom", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("chain B ibc denom", func(t *testing.T) { + actualBalance, err := s.QueryBalance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + }) + + t.Run("tokens are un-escrowed", func(t *testing.T) { + actualTotalEscrow, err := s.QueryTotalEscrowForDenom(ctx, chainA, chainADenom) + s.Require().NoError(err) + s.Require().Equal(sdk.NewCoin(chainADenom, sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because tokens have come back + }) +} + // TestMsgTransfer_Fails_InvalidAddress attempts to send an IBC transfer to an invalid address and ensures // that the tokens on the sending chain are unescrowed. func (s *TransferTestSuite) TestMsgTransfer_Fails_InvalidAddress() { t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions()) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) chainA, chainB := s.GetChains() chainADenom := chainA.Config().Denom @@ -169,7 +270,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Fails_InvalidAddress() { s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") t.Run("native IBC token transfer from chainA to invalid address", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -202,7 +303,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Timeout_Nonincentivized() { t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions()) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) chainA, _ := s.GetChains() chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) @@ -250,7 +351,7 @@ func (s *TransferTestSuite) TestSendEnabledParam() { t := s.T() ctx := context.TODO() - _, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions()) + _, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) chainA, chainB := s.GetChains() chainADenom := chainA.Config().Denom @@ -276,7 +377,7 @@ func (s *TransferTestSuite) TestSendEnabledParam() { }) t.Run("ensure packets can be sent", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -300,7 +401,7 @@ func (s *TransferTestSuite) TestSendEnabledParam() { }) t.Run("ensure ics20 transfer fails", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") s.AssertTxFailure(transferTxResp, transfertypes.ErrSendDisabled) }) } @@ -310,7 +411,7 @@ func (s *TransferTestSuite) TestReceiveEnabledParam() { t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions()) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) chainA, chainB := s.GetChains() chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) @@ -340,7 +441,7 @@ func (s *TransferTestSuite) TestReceiveEnabledParam() { t.Run("ensure packets can be received, send from chainB to chainA", func(t *testing.T) { t.Run("send from chainB to chainA", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferAmount(chainBDenom), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") + transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainBDenom)), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -392,7 +493,7 @@ func (s *TransferTestSuite) TestReceiveEnabledParam() { t.Run("ensure ics20 transfer fails", func(t *testing.T) { t.Run("send from chainB to chainA", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferAmount(chainBDenom), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") + transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainBDenom)), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -429,7 +530,7 @@ func (s *TransferTestSuite) TestMsgTransfer_WithMemo() { t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions()) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) chainA, chainB := s.GetChains() chainADenom := chainA.Config().Denom @@ -443,7 +544,7 @@ func (s *TransferTestSuite) TestMsgTransfer_WithMemo() { s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") t.Run("IBC token transfer with memo from chainA to chainB", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "memo") + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "memo") s.AssertTxSuccess(transferTxResp) }) diff --git a/e2e/tests/transfer/localhost_test.go b/e2e/tests/transfer/localhost_test.go index a8909fa9675..8b806ce2fcf 100644 --- a/e2e/tests/transfer/localhost_test.go +++ b/e2e/tests/transfer/localhost_test.go @@ -34,7 +34,7 @@ func (s *LocalhostTransferTestSuite) TestMsgTransfer_Localhost() { t := s.T() ctx := context.TODO() - _, _ = s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions()) + _, _ = s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) chainA, _ := s.GetChains() chainADenom := chainA.Config().Denom @@ -130,7 +130,7 @@ func (s *LocalhostTransferTestSuite) TestMsgTransfer_Localhost() { t.Run("send packet localhost ibc transfer", func(t *testing.T) { var err error - txResp := s.Transfer(ctx, chainA, userAWallet, transfertypes.PortID, msgChanOpenInitRes.ChannelId, testvalues.DefaultTransferAmount(chainADenom), userAWallet.FormattedAddress(), userBWallet.FormattedAddress(), clienttypes.NewHeight(1, 100), 0, "") + txResp := s.Transfer(ctx, chainA, userAWallet, transfertypes.PortID, msgChanOpenInitRes.ChannelId, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), userAWallet.FormattedAddress(), userBWallet.FormattedAddress(), clienttypes.NewHeight(1, 100), 0, "") s.AssertTxSuccess(txResp) packet, err = ibctesting.ParsePacketFromEvents(txResp.Events) diff --git a/e2e/tests/transfer/upgrades_test.go b/e2e/tests/transfer/upgrades_test.go index 74f5593d611..c3382ea2e71 100644 --- a/e2e/tests/transfer/upgrades_test.go +++ b/e2e/tests/transfer/upgrades_test.go @@ -35,7 +35,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_ t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions()) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) channelB := channelA.Counterparty chainA, chainB := s.GetChains() @@ -236,12 +236,146 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_ }) } +// TestChannelUpgrade_WithICS20v2_Succeeds tests upgrading a transfer channel to ICS20 v2. +func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succeeds() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version1)) + channelB := channelA.Counterparty + chainA, chainB := s.GetChains() + + chainADenom := chainA.Config().Denom + chainBDenom := chainB.Config().Denom + chainAIBCToken := testsuite.GetIBCToken(chainBDenom, channelA.PortID, channelA.ChannelID) + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelB.PortID, channelB.ChannelID) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("verify transfer version of channel A is ics20-1", func(t *testing.T) { + channel, err := s.QueryChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Equal(transfertypes.Version1, channel.Version, "the channel version is not ics20-1") + }) + + t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + chainBWalletAmount := ibc.WalletAmount{ + Address: chainBWallet.FormattedAddress(), // destination address + Denom: chainA.Config().Denom, + Amount: sdkmath.NewInt(testvalues.IBCTransferAmount), + } + + transferTxResp, err := chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainBWalletAmount, ibc.TransferOptions{}) + s.Require().NoError(err) + s.Require().NoError(transferTxResp.Validate(), "chain-a ibc transfer tx is invalid") + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + 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) + + actualTotalEscrow, err := s.QueryTotalEscrowForDenom(ctx, chainA, chainADenom) + s.Require().NoError(err) + + expectedTotalEscrow := sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.IBCTransferAmount)) + s.Require().Equal(expectedTotalEscrow, actualTotalEscrow) + }) + + 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 := s.QueryBalance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) { + chA, err := s.QueryChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + + upgradeFields := channeltypes.NewUpgradeFields(chA.Ordering, chA.ConnectionHops, transfertypes.Version1) + s.InitiateChannelUpgrade(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, upgradeFields) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") + + t.Run("verify channel A upgraded and transfer version is ics20-2", func(t *testing.T) { + channel, err := s.QueryChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Equal(transfertypes.Version1, channel.Version, "the channel version is not ics20-2") + }) + + t.Run("verify channel B upgraded and transfer version is ics20-2", func(t *testing.T) { + channel, err := s.QueryChannel(ctx, chainB, channelB.PortID, channelB.ChannelID) + s.Require().NoError(err) + s.Require().Equal(transfertypes.Version1, channel.Version, "the channel version is not ics20-2") + }) + + // send the native chainB denom and also the ibc token from chainA + denoms := []string{chainBIBCToken.IBCDenom(), chainBDenom} + var transferCoins []sdk.Coin + for _, denom := range denoms { + transferCoins = append(transferCoins, testvalues.DefaultTransferAmount(denom)) + } + + t.Run("native token from chain B and non-native IBC token from chainA, both to chainA", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, transferCoins, chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") + s.AssertTxSuccess(transferTxResp) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainB, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) + + t.Run("chain A native denom", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("chain B ibc denom", func(t *testing.T) { + actualBalance, err := s.QueryBalance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + }) + + t.Run("tokens are un-escrowed", func(t *testing.T) { + actualTotalEscrow, err := s.QueryTotalEscrowForDenom(ctx, chainA, chainADenom) + s.Require().NoError(err) + s.Require().Equal(sdk.NewCoin(chainADenom, sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because tokens have come back + }) + +} + // TestChannelUpgrade_WithFeeMiddleware_CrossingHello_Succeeds tests upgrading a transfer channel to wire up fee middleware under crossing hello func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_CrossingHello_Succeeds() { t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions()) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) channelB := channelA.Counterparty chainA, chainB := s.GetChains() @@ -257,7 +391,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_ // trying to create some inflight packets, although they might get relayed before the upgrade starts t.Run("create inflight transfer packets between chain A and chain B", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -330,7 +464,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_ t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions()) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) channelB := channelA.Counterparty chainA, chainB := s.GetChains() @@ -361,7 +495,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_ s.Require().NoError(err) s.Require().Equal(channeltypes.OPEN, channel.State, "the channel state is not OPEN") - s.Require().Equal(transfertypes.Version, channel.Version, "the channel version is not ics20-1") + s.Require().Equal(transfertypes.Version, channel.Version, "the channel version is not ics20-2") errorReceipt, err := s.QueryUpgradeError(ctx, chainA, channelA.PortID, channelA.ChannelID) s.Require().NoError(err) @@ -374,7 +508,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_ s.Require().NoError(err) s.Require().Equal(channeltypes.OPEN, channel.State, "the channel state is not OPEN") - s.Require().Equal(transfertypes.Version, channel.Version, "the channel version is not ics20-1") + s.Require().Equal(transfertypes.Version, channel.Version, "the channel version is not ics20-2") errorReceipt, err := s.QueryUpgradeError(ctx, chainB, channelB.PortID, channelB.ChannelID) s.Require().NoError(err) diff --git a/e2e/tests/upgrades/genesis_test.go b/e2e/tests/upgrades/genesis_test.go index 2e2d1850e03..85d860a6adb 100644 --- a/e2e/tests/upgrades/genesis_test.go +++ b/e2e/tests/upgrades/genesis_test.go @@ -68,7 +68,7 @@ func (s *GenesisTestSuite) TestIBCGenesis() { s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") t.Run("ics20: native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -129,7 +129,7 @@ func (s *GenesisTestSuite) TestIBCGenesis() { }) t.Run("ics20: native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") s.AssertTxSuccess(transferTxResp) }) diff --git a/e2e/tests/upgrades/upgrade_test.go b/e2e/tests/upgrades/upgrade_test.go index 494a73a4511..eadf73df006 100644 --- a/e2e/tests/upgrades/upgrade_test.go +++ b/e2e/tests/upgrades/upgrade_test.go @@ -147,7 +147,7 @@ func (s *UpgradeTestSuite) TestIBCChainUpgrade() { s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -186,7 +186,7 @@ func (s *UpgradeTestSuite) TestIBCChainUpgrade() { }) t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -204,7 +204,7 @@ func (s *UpgradeTestSuite) TestIBCChainUpgrade() { t.Run("ensure packets can be received, send from chainB to chainA", func(t *testing.T) { t.Run("send from chainB to chainA", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferAmount(chainBDenom), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") + transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainBDenom)), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -361,7 +361,7 @@ func (s *UpgradeTestSuite) TestV6ToV7ChainUpgrade() { 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 := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -414,7 +414,7 @@ func (s *UpgradeTestSuite) TestV6ToV7ChainUpgrade() { }) 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 := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB.(*cosmos.CosmosChain)), 0, "") + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB.(*cosmos.CosmosChain)), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -454,7 +454,7 @@ func (s *UpgradeTestSuite) TestV7ToV7_1ChainUpgrade() { s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") t.Run("transfer native tokens from chainA to chainB", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB.(*cosmos.CosmosChain)), 0, "") + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB.(*cosmos.CosmosChain)), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -511,7 +511,7 @@ func (s *UpgradeTestSuite) TestV7ToV7_1ChainUpgrade() { }) 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 := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -545,7 +545,7 @@ func (s *UpgradeTestSuite) TestV7ToV8ChainUpgrade() { s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") t.Run("transfer native tokens from chainA to chainB", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -605,7 +605,7 @@ func (s *UpgradeTestSuite) TestV7ToV8ChainUpgrade() { }) 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 := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -638,7 +638,7 @@ func (s *UpgradeTestSuite) TestV8ToV8_1ChainUpgrade() { s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") t.Run("transfer native tokens from chainA to chainB", func(t *testing.T) { - txResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + txResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") s.AssertTxSuccess(txResp) }) @@ -731,7 +731,7 @@ func (s *UpgradeTestSuite) TestV8ToV8_1ChainUpgrade() { s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA), "failed to wait for blocks") 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 := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") s.AssertTxSuccess(transferTxResp) }) @@ -751,7 +751,7 @@ func (s *UpgradeTestSuite) TestV8ToV8_1ChainUpgrade_ChannelUpgrades() { testCfg := testsuite.LoadConfig() ctx := context.Background() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions()) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) channelB := channelA.Counterparty chainA, chainB := s.GetChains() diff --git a/e2e/testsuite/testsuite.go b/e2e/testsuite/testsuite.go index dc188180aa1..cbc8b9adf9c 100644 --- a/e2e/testsuite/testsuite.go +++ b/e2e/testsuite/testsuite.go @@ -399,9 +399,9 @@ func (s *E2ETestSuite) GetRelayerExecReporter() *testreporter.RelayerExecReporte } // TransferChannelOptions configures both of the chains to have non-incentivized transfer channels. -func (*E2ETestSuite) TransferChannelOptions() func(options *ibc.CreateChannelOptions) { +func (*E2ETestSuite) TransferChannelOptions(version string) func(options *ibc.CreateChannelOptions) { return func(opts *ibc.CreateChannelOptions) { - opts.Version = transfertypes.Version + opts.Version = version opts.SourcePortName = transfertypes.PortID opts.DestPortName = transfertypes.PortID } @@ -431,7 +431,7 @@ func (s *E2ETestSuite) GetTimeoutHeight(ctx context.Context, chain ibc.Chain) cl return clienttypes.NewHeight(clienttypes.ParseChainID(chain.Config().ChainID), uint64(height)+1000) } -// CreateUpgradeFields creates upgrade fields for channel with fee middleware +// CreateUpgradeFields creates upgrade fields for channel with fee middleware. func (s *E2ETestSuite) CreateUpgradeFields(channel channeltypes.Channel) channeltypes.UpgradeFields { versionMetadata := feetypes.Metadata{ FeeVersion: feetypes.Version, diff --git a/e2e/testsuite/tx.go b/e2e/testsuite/tx.go index b0ee8ab763b..fcf31c1f8d4 100644 --- a/e2e/testsuite/tx.go +++ b/e2e/testsuite/tx.go @@ -263,9 +263,9 @@ func (s *E2ETestSuite) ExecuteGovV1Beta1Proposal(ctx context.Context, chain ibc. // Transfer broadcasts a MsgTransfer message. func (s *E2ETestSuite) Transfer(ctx context.Context, chain ibc.Chain, user ibc.Wallet, - portID, channelID string, token sdk.Coin, sender, receiver string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, memo string, + portID, channelID string, tokens sdk.Coins, sender, receiver string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, memo string, ) sdk.TxResponse { - msg := transfertypes.NewMsgTransfer(portID, channelID, sdk.NewCoins(token), sender, receiver, timeoutHeight, timeoutTimestamp, memo) + msg := transfertypes.NewMsgTransfer(portID, channelID, tokens, sender, receiver, timeoutHeight, timeoutTimestamp, memo) return s.BroadcastMessages(ctx, chain, user, msg) } From b09f5d7cac8c835b400d726581913ff1c6964ca6 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Sun, 12 May 2024 21:05:14 +0200 Subject: [PATCH 13/42] some fixes --- e2e/tests/core/02-client/client_test.go | 1 + e2e/tests/core/03-connection/connection_test.go | 1 + e2e/tests/transfer/localhost_test.go | 1 + e2e/tests/transfer/upgrades_test.go | 6 +++--- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/e2e/tests/core/02-client/client_test.go b/e2e/tests/core/02-client/client_test.go index b2276ef64e7..722827f1c72 100644 --- a/e2e/tests/core/02-client/client_test.go +++ b/e2e/tests/core/02-client/client_test.go @@ -33,6 +33,7 @@ import ( "github.com/cosmos/ibc-go/e2e/testsuite" "github.com/cosmos/ibc-go/e2e/testvalues" wasmtypes "github.com/cosmos/ibc-go/modules/light-clients/08-wasm/types" + transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" diff --git a/e2e/tests/core/03-connection/connection_test.go b/e2e/tests/core/03-connection/connection_test.go index b2c08856f38..1c56af9a0fa 100644 --- a/e2e/tests/core/03-connection/connection_test.go +++ b/e2e/tests/core/03-connection/connection_test.go @@ -20,6 +20,7 @@ import ( "github.com/cosmos/ibc-go/e2e/testsuite" "github.com/cosmos/ibc-go/e2e/testvalues" + transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" ibctesting "github.com/cosmos/ibc-go/v8/testing" diff --git a/e2e/tests/transfer/localhost_test.go b/e2e/tests/transfer/localhost_test.go index 8b806ce2fcf..073517a686a 100644 --- a/e2e/tests/transfer/localhost_test.go +++ b/e2e/tests/transfer/localhost_test.go @@ -9,6 +9,7 @@ import ( test "github.com/strangelove-ventures/interchaintest/v8/testutil" testifysuite "github.com/stretchr/testify/suite" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/ibc-go/e2e/testsuite" "github.com/cosmos/ibc-go/e2e/testvalues" transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" diff --git a/e2e/tests/transfer/upgrades_test.go b/e2e/tests/transfer/upgrades_test.go index c3382ea2e71..273ecaa4a69 100644 --- a/e2e/tests/transfer/upgrades_test.go +++ b/e2e/tests/transfer/upgrades_test.go @@ -310,7 +310,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succee chA, err := s.QueryChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) s.Require().NoError(err) - upgradeFields := channeltypes.NewUpgradeFields(chA.Ordering, chA.ConnectionHops, transfertypes.Version1) + upgradeFields := channeltypes.NewUpgradeFields(chA.Ordering, chA.ConnectionHops, transfertypes.Version) s.InitiateChannelUpgrade(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, upgradeFields) }) @@ -319,13 +319,13 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succee t.Run("verify channel A upgraded and transfer version is ics20-2", func(t *testing.T) { channel, err := s.QueryChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) s.Require().NoError(err) - s.Require().Equal(transfertypes.Version1, channel.Version, "the channel version is not ics20-2") + s.Require().Equal(transfertypes.Version, channel.Version, "the channel version is not ics20-2") }) t.Run("verify channel B upgraded and transfer version is ics20-2", func(t *testing.T) { channel, err := s.QueryChannel(ctx, chainB, channelB.PortID, channelB.ChannelID) s.Require().NoError(err) - s.Require().Equal(transfertypes.Version1, channel.Version, "the channel version is not ics20-2") + s.Require().Equal(transfertypes.Version, channel.Version, "the channel version is not ics20-2") }) // send the native chainB denom and also the ibc token from chainA From 4c333c5b9b442187bb0f4b378f772ee54c4a6044 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Tue, 14 May 2024 10:50:56 +0200 Subject: [PATCH 14/42] changes to transfer tx CLI to support multiple denoms --- modules/apps/transfer/client/cli/tx.go | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/modules/apps/transfer/client/cli/tx.go b/modules/apps/transfer/client/cli/tx.go index ccdd08af588..b0ca95fe7fe 100644 --- a/modules/apps/transfer/client/cli/tx.go +++ b/modules/apps/transfer/client/cli/tx.go @@ -34,12 +34,14 @@ var defaultRelativePacketTimeoutTimestamp = uint64((time.Duration(10) * time.Min // NewTransferTxCmd returns the command to create a NewMsgTransfer transaction func NewTransferTxCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "transfer [src-port] [src-channel] [receiver] [amount]", - Short: "Transfer a fungible token through IBC", - Long: strings.TrimSpace(`Transfer a fungible token through IBC. Timeouts can be specified as absolute using the {absolute-timeouts} flag. -Timeout height can be set by passing in the height string in the form {revision}-{height} using the {packet-timeout-height} flag. Note, relative timeout height is not supported. -Relative timeout timestamp is added to the value of the user's local system clock time using the {packet-timeout-timestamp} flag. If no timeout value is set then a default relative timeout value of 10 minutes is used.`), - Example: fmt.Sprintf("%s tx ibc-transfer transfer [src-port] [src-channel] [receiver] [amount]", version.AppName), + Use: "transfer [src-port] [src-channel] [receiver] [coins]", + Short: "Transfer one or more fungible tokens through IBC", + Long: strings.TrimSpace(`Transfer one or more fungible tokens through IBC. Multiple tokens can be transferred in a single +packet if the coins list is a comma-separated string (e.g. 100uatom,100uosmo). Timeouts can be specified as absolute using the {absolute-timeouts} flag. +Timeout height can be set by passing in the height string in the form {revision}-{height} using the {packet-timeout-height} flag. +Note, relative timeout height is not supported. Relative timeout timestamp is added to the value of the user's local system clock time +using the {packet-timeout-timestamp} flag. If no timeout value is set then a default relative timeout value of 10 minutes is used.`), + Example: fmt.Sprintf("%s tx ibc-transfer transfer [src-port] [src-channel] [receiver] [coins]", version.AppName), Args: cobra.ExactArgs(4), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) @@ -51,14 +53,16 @@ Relative timeout timestamp is added to the value of the user's local system cloc srcChannel := args[1] receiver := args[2] - coin, err := sdk.ParseCoinNormalized(args[3]) + coins, err := sdk.ParseCoinsNormalized(args[3]) if err != nil { return err } - if !strings.HasPrefix(coin.Denom, "ibc/") { - denomTrace := types.ParseDenomTrace(coin.Denom) - coin.Denom = denomTrace.IBCDenom() + for i, coin := range coins { + if !strings.HasPrefix(coin.Denom, "ibc/") { + denomTrace := types.ParseDenomTrace(coin.Denom) + coins[i].Denom = denomTrace.IBCDenom() + } } timeoutHeightStr, err := cmd.Flags().GetString(flagPacketTimeoutHeight) @@ -107,7 +111,7 @@ Relative timeout timestamp is added to the value of the user's local system cloc } msg := types.NewMsgTransfer( - srcPort, srcChannel, sdk.NewCoins(coin), sender, receiver, timeoutHeight, timeoutTimestamp, memo, + srcPort, srcChannel, coins, sender, receiver, timeoutHeight, timeoutTimestamp, memo, ) return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, From 8b5529522e743165b3a7619d44f260a48a2eb6d6 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Tue, 14 May 2024 11:15:55 +0200 Subject: [PATCH 15/42] lint --- e2e/tests/core/02-client/client_test.go | 3 +-- e2e/tests/core/03-connection/connection_test.go | 3 +-- e2e/tests/transfer/base_test.go | 14 +++++++------- e2e/tests/transfer/localhost_test.go | 3 ++- e2e/tests/transfer/upgrades_test.go | 7 +++---- e2e/tests/upgrades/upgrade_test.go | 2 +- 6 files changed, 15 insertions(+), 17 deletions(-) diff --git a/e2e/tests/core/02-client/client_test.go b/e2e/tests/core/02-client/client_test.go index 722827f1c72..6b41da736b2 100644 --- a/e2e/tests/core/02-client/client_test.go +++ b/e2e/tests/core/02-client/client_test.go @@ -33,7 +33,6 @@ import ( "github.com/cosmos/ibc-go/e2e/testsuite" "github.com/cosmos/ibc-go/e2e/testvalues" wasmtypes "github.com/cosmos/ibc-go/modules/light-clients/08-wasm/types" - transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" @@ -437,7 +436,7 @@ func (s *ClientTestSuite) TestAllowedClientsParam() { t := s.T() ctx := context.TODO() - _, _ = s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) + _, _ = s.SetupChainsRelayerAndChannel(ctx, nil) chainA, chainB := s.GetChains() chainAVersion := chainA.Config().Images[0].Version diff --git a/e2e/tests/core/03-connection/connection_test.go b/e2e/tests/core/03-connection/connection_test.go index 1c56af9a0fa..77ddad31ebf 100644 --- a/e2e/tests/core/03-connection/connection_test.go +++ b/e2e/tests/core/03-connection/connection_test.go @@ -20,7 +20,6 @@ import ( "github.com/cosmos/ibc-go/e2e/testsuite" "github.com/cosmos/ibc-go/e2e/testvalues" - transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" ibctesting "github.com/cosmos/ibc-go/v8/testing" @@ -64,7 +63,7 @@ func (s *ConnectionTestSuite) TestMaxExpectedTimePerBlockParam() { t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, nil) chainA, chainB := s.GetChains() chainAVersion := chainA.Config().Images[0].Version diff --git a/e2e/tests/transfer/base_test.go b/e2e/tests/transfer/base_test.go index d660fe532b9..d2db2122f47 100644 --- a/e2e/tests/transfer/base_test.go +++ b/e2e/tests/transfer/base_test.go @@ -47,7 +47,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized() { t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, nil) chainA, chainB := s.GetChains() chainADenom := chainA.Config().Denom @@ -158,7 +158,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom( t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, nil) chainA, chainB := s.GetChains() chainADenom := chainA.Config().Denom @@ -259,7 +259,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Fails_InvalidAddress() { t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, nil) chainA, chainB := s.GetChains() chainADenom := chainA.Config().Denom @@ -303,7 +303,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Timeout_Nonincentivized() { t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, nil) chainA, _ := s.GetChains() chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) @@ -351,7 +351,7 @@ func (s *TransferTestSuite) TestSendEnabledParam() { t := s.T() ctx := context.TODO() - _, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) + _, channelA := s.SetupChainsRelayerAndChannel(ctx, nil) chainA, chainB := s.GetChains() chainADenom := chainA.Config().Denom @@ -411,7 +411,7 @@ func (s *TransferTestSuite) TestReceiveEnabledParam() { t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, nil) chainA, chainB := s.GetChains() chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) @@ -530,7 +530,7 @@ func (s *TransferTestSuite) TestMsgTransfer_WithMemo() { t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, nil) chainA, chainB := s.GetChains() chainADenom := chainA.Config().Denom diff --git a/e2e/tests/transfer/localhost_test.go b/e2e/tests/transfer/localhost_test.go index 073517a686a..8f63d3fcf12 100644 --- a/e2e/tests/transfer/localhost_test.go +++ b/e2e/tests/transfer/localhost_test.go @@ -10,6 +10,7 @@ import ( testifysuite "github.com/stretchr/testify/suite" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ibc-go/e2e/testsuite" "github.com/cosmos/ibc-go/e2e/testvalues" transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" @@ -35,7 +36,7 @@ func (s *LocalhostTransferTestSuite) TestMsgTransfer_Localhost() { t := s.T() ctx := context.TODO() - _, _ = s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) + _, _ = s.SetupChainsRelayerAndChannel(ctx, nil) chainA, _ := s.GetChains() chainADenom := chainA.Config().Denom diff --git a/e2e/tests/transfer/upgrades_test.go b/e2e/tests/transfer/upgrades_test.go index 273ecaa4a69..d820b8bea56 100644 --- a/e2e/tests/transfer/upgrades_test.go +++ b/e2e/tests/transfer/upgrades_test.go @@ -35,7 +35,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_ t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, nil) channelB := channelA.Counterparty chainA, chainB := s.GetChains() @@ -367,7 +367,6 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succee s.Require().NoError(err) s.Require().Equal(sdk.NewCoin(chainADenom, sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because tokens have come back }) - } // TestChannelUpgrade_WithFeeMiddleware_CrossingHello_Succeeds tests upgrading a transfer channel to wire up fee middleware under crossing hello @@ -375,7 +374,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_ t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, nil) channelB := channelA.Counterparty chainA, chainB := s.GetChains() @@ -464,7 +463,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_ t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, nil) channelB := channelA.Counterparty chainA, chainB := s.GetChains() diff --git a/e2e/tests/upgrades/upgrade_test.go b/e2e/tests/upgrades/upgrade_test.go index eadf73df006..1564b906dd9 100644 --- a/e2e/tests/upgrades/upgrade_test.go +++ b/e2e/tests/upgrades/upgrade_test.go @@ -751,7 +751,7 @@ func (s *UpgradeTestSuite) TestV8ToV8_1ChainUpgrade_ChannelUpgrades() { testCfg := testsuite.LoadConfig() ctx := context.Background() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version)) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, nil) channelB := channelA.Counterparty chainA, chainB := s.GetChains() From 0478cb9f7457985fdc6d220142d1e23b87e62d5d Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Tue, 14 May 2024 12:06:12 +0200 Subject: [PATCH 16/42] import renaming --- modules/apps/callbacks/ibc_middleware_test.go | 14 +++++++------- modules/apps/transfer/ibc_module.go | 8 ++++---- modules/apps/transfer/ibc_module_test.go | 18 +++++++++--------- modules/apps/transfer/keeper/mbt_relay_test.go | 4 ++-- .../apps/transfer/keeper/msg_server_test.go | 2 +- modules/apps/transfer/keeper/relay.go | 4 ++-- modules/apps/transfer/keeper/relay_test.go | 14 +++++++------- modules/apps/transfer/types/keys.go | 2 +- 8 files changed, 33 insertions(+), 33 deletions(-) diff --git a/modules/apps/callbacks/ibc_middleware_test.go b/modules/apps/callbacks/ibc_middleware_test.go index c08f531ca3d..83db4a1b93f 100644 --- a/modules/apps/callbacks/ibc_middleware_test.go +++ b/modules/apps/callbacks/ibc_middleware_test.go @@ -13,7 +13,7 @@ import ( "github.com/cosmos/ibc-go/modules/apps/callbacks/testing/simapp" "github.com/cosmos/ibc-go/modules/apps/callbacks/types" icacontrollertypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/controller/types" - transferv1types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" transferv3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channelkeeper "github.com/cosmos/ibc-go/v8/modules/core/04-channel/keeper" @@ -339,7 +339,7 @@ func (s *CallbacksTestSuite) TestOnAcknowledgementPacket() { tc.malleate() // callbacks module is routed as top level middleware - transferStack, ok := s.chainA.App.GetIBCKeeper().Router.GetRoute(transferv1types.ModuleName) + transferStack, ok := s.chainA.App.GetIBCKeeper().Router.GetRoute(transfertypes.ModuleName) s.Require().True(ok) onAcknowledgementPacket := func() error { @@ -478,7 +478,7 @@ func (s *CallbacksTestSuite) TestOnTimeoutPacket() { // succeed on timeout userGasLimit := 600_000 timeoutTimestamp := uint64(s.chainB.GetContext().BlockTime().UnixNano()) - msg := transferv1types.NewMsgTransfer( + msg := transfertypes.NewMsgTransfer( s.path.EndpointA.ChannelConfig.PortID, s.path.EndpointA.ChannelID, sdk.NewCoins(ibctesting.TestCoin), s.chainA.SenderAccount.GetAddress().String(), s.chainB.SenderAccount.GetAddress().String(), clienttypes.ZeroHeight(), timeoutTimestamp, @@ -502,7 +502,7 @@ func (s *CallbacksTestSuite) TestOnTimeoutPacket() { tc.malleate() // callbacks module is routed as top level middleware - transferStack, ok := s.chainA.App.GetIBCKeeper().Router.GetRoute(transferv1types.ModuleName) + transferStack, ok := s.chainA.App.GetIBCKeeper().Router.GetRoute(transfertypes.ModuleName) s.Require().True(ok) onTimeoutPacket := func() error { @@ -670,7 +670,7 @@ func (s *CallbacksTestSuite) TestOnRecvPacket() { tc.malleate() // callbacks module is routed as top level middleware - transferStack, ok := s.chainB.App.GetIBCKeeper().Router.GetRoute(transferv1types.ModuleName) + transferStack, ok := s.chainB.App.GetIBCKeeper().Router.GetRoute(transfertypes.ModuleName) s.Require().True(ok) onRecvPacket := func() ibcexported.Acknowledgement { @@ -977,13 +977,13 @@ func (s *CallbacksTestSuite) TestUnmarshalPacketData() { // We will pass the function call down the transfer stack to the transfer module // transfer stack UnmarshalPacketData call order: callbacks -> fee -> transfer - transferStack, ok := s.chainA.App.GetIBCKeeper().Router.GetRoute(transferv1types.ModuleName) + transferStack, ok := s.chainA.App.GetIBCKeeper().Router.GetRoute(transfertypes.ModuleName) s.Require().True(ok) unmarshalerStack, ok := transferStack.(types.CallbacksCompatibleModule) s.Require().True(ok) - expPacketDataICS20V1 := transferv1types.FungibleTokenPacketData{ + expPacketDataICS20V1 := transfertypes.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), Sender: ibctesting.TestAccAddress, diff --git a/modules/apps/transfer/ibc_module.go b/modules/apps/transfer/ibc_module.go index 595c014f00b..7531a9318b5 100644 --- a/modules/apps/transfer/ibc_module.go +++ b/modules/apps/transfer/ibc_module.go @@ -15,7 +15,7 @@ import ( convertinternal "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/convert" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/keeper" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" - multidenom "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" + v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" host "github.com/cosmos/ibc-go/v8/modules/core/24-host" @@ -175,7 +175,7 @@ func (IBCModule) OnChanCloseConfirm( return nil } -func (IBCModule) unmarshalPacketDataBytesToICS20V2(bz []byte) (multidenom.FungibleTokenPacketData, error) { +func (IBCModule) unmarshalPacketDataBytesToICS20V2(bz []byte) (v3types.FungibleTokenPacketData, error) { // TODO: remove support for this function parsing v1 packet data // TODO: explicit check for packet data type against app version @@ -186,14 +186,14 @@ func (IBCModule) unmarshalPacketDataBytesToICS20V2(bz []byte) (multidenom.Fungib } } - var data multidenom.FungibleTokenPacketData + var data v3types.FungibleTokenPacketData if err := json.Unmarshal(bz, &data); err == nil { if len(data.Tokens) != 0 { return data, nil } } - return multidenom.FungibleTokenPacketData{}, errorsmod.Wrapf(ibcerrors.ErrInvalidType, "cannot unmarshal ICS-20 transfer packet data") + return v3types.FungibleTokenPacketData{}, errorsmod.Wrapf(ibcerrors.ErrInvalidType, "cannot unmarshal ICS-20 transfer packet data") } // OnRecvPacket implements the IBCModule interface. A successful acknowledgement diff --git a/modules/apps/transfer/ibc_module_test.go b/modules/apps/transfer/ibc_module_test.go index 4acb9b6501a..22b398f4d50 100644 --- a/modules/apps/transfer/ibc_module_test.go +++ b/modules/apps/transfer/ibc_module_test.go @@ -9,7 +9,7 @@ import ( capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" "github.com/cosmos/ibc-go/v8/modules/apps/transfer" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" - multidenom "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" + v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" @@ -544,8 +544,8 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { { "success: valid packet data multidenom with memo", func() { - initialPacketData = multidenom.FungibleTokenPacketData{ - Tokens: []*multidenom.Token{ + initialPacketData = v3types.FungibleTokenPacketData{ + Tokens: []*v3types.Token{ { Denom: "atom", Amount: ibctesting.TestCoin.Amount.String(), @@ -557,15 +557,15 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { Memo: "some memo", } - data = initialPacketData.(multidenom.FungibleTokenPacketData).GetBytes() + data = initialPacketData.(v3types.FungibleTokenPacketData).GetBytes() }, true, }, { "success: valid packet data multidenom without memo", func() { - initialPacketData = multidenom.FungibleTokenPacketData{ - Tokens: []*multidenom.Token{ + initialPacketData = v3types.FungibleTokenPacketData{ + Tokens: []*v3types.Token{ { Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), @@ -577,7 +577,7 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { Memo: "", } - data = initialPacketData.(multidenom.FungibleTokenPacketData).GetBytes() + data = initialPacketData.(v3types.FungibleTokenPacketData).GetBytes() }, true, }, @@ -600,7 +600,7 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { if tc.expPass { suite.Require().NoError(err) - v3PacketData, ok := packetData.(multidenom.FungibleTokenPacketData) + v3PacketData, ok := packetData.(v3types.FungibleTokenPacketData) suite.Require().True(ok) if v1PacketData, ok := initialPacketData.(types.FungibleTokenPacketData); ok { @@ -610,7 +610,7 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { suite.Require().Equal(v1PacketData.Receiver, v3PacketData.Receiver) suite.Require().Equal(v1PacketData.Memo, v3PacketData.Memo) } else { - suite.Require().Equal(initialPacketData.(multidenom.FungibleTokenPacketData), v3PacketData) + suite.Require().Equal(initialPacketData.(v3types.FungibleTokenPacketData), v3PacketData) } } else { suite.Require().Error(err) diff --git a/modules/apps/transfer/keeper/mbt_relay_test.go b/modules/apps/transfer/keeper/mbt_relay_test.go index d12abd8a589..74c74bc6343 100644 --- a/modules/apps/transfer/keeper/mbt_relay_test.go +++ b/modules/apps/transfer/keeper/mbt_relay_test.go @@ -19,7 +19,7 @@ import ( "github.com/cometbft/cometbft/crypto" - convert "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/convert" + convertinternal "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/convert" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" @@ -145,7 +145,7 @@ func BalancesFromTla(tla []TlaBalance) []Balance { } func FungibleTokenPacketFromTla(packet TlaFungibleTokenPacket) FungibleTokenPacket { - denom, trace := convert.ExtractDenomAndTraceFromV1Denom(DenomFromTla(packet.Data.Denom)) + denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(DenomFromTla(packet.Data.Denom)) return FungibleTokenPacket{ SourceChannel: packet.SourceChannel, SourcePort: packet.SourcePort, diff --git a/modules/apps/transfer/keeper/msg_server_test.go b/modules/apps/transfer/keeper/msg_server_test.go index 355b21e5b88..5ab8a03e069 100644 --- a/modules/apps/transfer/keeper/msg_server_test.go +++ b/modules/apps/transfer/keeper/msg_server_test.go @@ -37,7 +37,7 @@ func (suite *KeeperTestSuite) TestMsgTransfer() { false, }, { - "success: multi-denom with version ics20-2", + "success: multidenom", func() { coin2 = sdk.NewCoin("bond", sdkmath.NewInt(100)) coins := sdk.NewCoins(coin1, coin2) diff --git a/modules/apps/transfer/keeper/relay.go b/modules/apps/transfer/keeper/relay.go index 0671d64fffd..fa0e9db7635 100644 --- a/modules/apps/transfer/keeper/relay.go +++ b/modules/apps/transfer/keeper/relay.go @@ -12,7 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/convert" + convertinternal "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/convert" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" @@ -135,7 +135,7 @@ func (k Keeper) sendTransfer( } } - denom, trace := convert.ExtractDenomAndTraceFromV1Denom(fullDenomPath) + denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(fullDenomPath) token := &v3types.Token{ Denom: denom, Amount: coin.Amount.String(), diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index e4ce18817fb..822cae0506a 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -10,7 +10,7 @@ import ( banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/convert" + convertinternal "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/convert" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" @@ -411,7 +411,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { tc.malleate() - denom, trace := convert.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) + denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) data := v3types.NewFungibleTokenPacketData( []*v3types.Token{ { @@ -494,7 +494,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacketSetsTotalEscrowAmountForSourceIBCT Path: fmt.Sprintf("%s/%s/%s/%s", path2.EndpointA.ChannelConfig.PortID, path2.EndpointA.ChannelID, path1.EndpointB.ChannelConfig.PortID, path1.EndpointB.ChannelID), } - denom, trace := convert.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) + denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) data := v3types.NewFungibleTokenPacketData( []*v3types.Token{ { @@ -621,7 +621,7 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { tc.malleate() - denom, trace := convert.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) + denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) data := v3types.NewFungibleTokenPacketData( []*v3types.Token{ { @@ -714,7 +714,7 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacketSetsTotalEscrowAmountFo ), ) - denom, trace := convert.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) + denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) data := v3types.NewFungibleTokenPacketData( []*v3types.Token{ { @@ -837,7 +837,7 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() { tc.malleate() - denom, trace := convert.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) + denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) data := v3types.NewFungibleTokenPacketData( []*v3types.Token{ { @@ -923,7 +923,7 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacketSetsTotalEscrowAmountForSourceI ), ) - denom, trace := convert.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) + denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) data := v3types.NewFungibleTokenPacketData( []*v3types.Token{ { diff --git a/modules/apps/transfer/types/keys.go b/modules/apps/transfer/types/keys.go index d985438ab71..e44d521856a 100644 --- a/modules/apps/transfer/types/keys.go +++ b/modules/apps/transfer/types/keys.go @@ -43,7 +43,7 @@ const ( Version1 = "ics20-1" // escrowAddressVersion should remain as ics20-1 to avoid the address changing. - escrowAddressVersion = "ics20-1" + escrowAddressVersion = Version1 ) var ( From f39d173f8405f97c1c05338cc61246f1392ddb92 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Tue, 21 May 2024 11:43:05 +0100 Subject: [PATCH 17/42] Use type with V2 suffix for package data (#6330) --- modules/apps/callbacks/ibc_middleware_test.go | 31 +- modules/apps/transfer/ibc_module.go | 9 +- modules/apps/transfer/ibc_module_test.go | 25 +- .../apps/transfer/internal/convert/convert.go | 9 +- .../transfer/internal/convert/convert_test.go | 41 +- .../apps/transfer/keeper/mbt_relay_test.go | 7 +- modules/apps/transfer/keeper/relay.go | 15 +- modules/apps/transfer/keeper/relay_test.go | 25 +- modules/apps/transfer/types/packet.go | 95 +++ modules/apps/transfer/types/packet.pb.go | 660 ++++++++++++++- modules/apps/transfer/types/packet_test.go | 409 +++++++++ modules/apps/transfer/types/{v3 => }/token.go | 7 +- .../transfer/types/{v3 => }/token_test.go | 25 +- modules/apps/transfer/types/v3/packet.go | 114 --- modules/apps/transfer/types/v3/packet.pb.go | 776 ------------------ modules/apps/transfer/types/v3/packet_test.go | 436 ---------- .../ibc/applications/transfer/v2/packet.proto | 24 + .../ibc/applications/transfer/v3/packet.proto | 29 - 18 files changed, 1269 insertions(+), 1468 deletions(-) rename modules/apps/transfer/types/{v3 => }/token.go (79%) rename modules/apps/transfer/types/{v3 => }/token_test.go (83%) delete mode 100644 modules/apps/transfer/types/v3/packet.go delete mode 100644 modules/apps/transfer/types/v3/packet.pb.go delete mode 100644 modules/apps/transfer/types/v3/packet_test.go delete mode 100644 proto/ibc/applications/transfer/v3/packet.proto diff --git a/modules/apps/callbacks/ibc_middleware_test.go b/modules/apps/callbacks/ibc_middleware_test.go index 83db4a1b93f..441512d7469 100644 --- a/modules/apps/callbacks/ibc_middleware_test.go +++ b/modules/apps/callbacks/ibc_middleware_test.go @@ -14,7 +14,6 @@ import ( "github.com/cosmos/ibc-go/modules/apps/callbacks/types" icacontrollertypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/controller/types" transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" - transferv3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channelkeeper "github.com/cosmos/ibc-go/v8/modules/core/04-channel/keeper" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" @@ -94,7 +93,7 @@ func (s *CallbacksTestSuite) TestWithICS4Wrapper() { } func (s *CallbacksTestSuite) TestSendPacket() { - var packetData transferv3types.FungibleTokenPacketData + var packetData transfertypes.FungibleTokenPacketDataV2 testCases := []struct { name string @@ -165,8 +164,8 @@ func (s *CallbacksTestSuite) TestSendPacket() { transferICS4Wrapper := GetSimApp(s.chainA).TransferKeeper.GetICS4Wrapper() - packetData = transferv3types.NewFungibleTokenPacketData( - []*transferv3types.Token{ + packetData = transfertypes.NewFungibleTokenPacketDataV2( + []*transfertypes.Token{ { Denom: ibctesting.TestCoin.GetDenom(), Amount: ibctesting.TestCoin.Amount.String(), @@ -231,7 +230,7 @@ func (s *CallbacksTestSuite) TestOnAcknowledgementPacket() { ) var ( - packetData transferv3types.FungibleTokenPacketData + packetData transfertypes.FungibleTokenPacketDataV2 packet channeltypes.Packet ack []byte ctx sdk.Context @@ -307,8 +306,8 @@ func (s *CallbacksTestSuite) TestOnAcknowledgementPacket() { s.SetupTransferTest() userGasLimit = 600000 - packetData = transferv3types.NewFungibleTokenPacketData( - []*transferv3types.Token{ + packetData = transfertypes.NewFungibleTokenPacketDataV2( + []*transfertypes.Token{ { Denom: ibctesting.TestCoin.GetDenom(), Amount: ibctesting.TestCoin.Amount.String(), @@ -401,7 +400,7 @@ func (s *CallbacksTestSuite) TestOnTimeoutPacket() { ) var ( - packetData transferv3types.FungibleTokenPacketData + packetData transfertypes.FungibleTokenPacketDataV2 packet channeltypes.Packet ctx sdk.Context ) @@ -563,7 +562,7 @@ func (s *CallbacksTestSuite) TestOnRecvPacket() { ) var ( - packetData transferv3types.FungibleTokenPacketData + packetData transfertypes.FungibleTokenPacketDataV2 packet channeltypes.Packet ctx sdk.Context userGasLimit uint64 @@ -640,8 +639,8 @@ func (s *CallbacksTestSuite) TestOnRecvPacket() { // set user gas limit above panic level in mock contract keeper userGasLimit = 600_000 - packetData = transferv3types.NewFungibleTokenPacketData( - []*transferv3types.Token{ + packetData = transfertypes.NewFungibleTokenPacketDataV2( + []*transfertypes.Token{ { Denom: ibctesting.TestCoin.GetDenom(), Amount: ibctesting.TestCoin.Amount.String(), @@ -725,7 +724,7 @@ func (s *CallbacksTestSuite) TestOnRecvPacket() { func (s *CallbacksTestSuite) TestWriteAcknowledgement() { var ( - packetData transferv3types.FungibleTokenPacketData + packetData transfertypes.FungibleTokenPacketDataV2 packet channeltypes.Packet ctx sdk.Context ack ibcexported.Acknowledgement @@ -772,8 +771,8 @@ func (s *CallbacksTestSuite) TestWriteAcknowledgement() { s.SetupTransferTest() // set user gas limit above panic level in mock contract keeper - packetData = transferv3types.NewFungibleTokenPacketData( - []*transferv3types.Token{ + packetData = transfertypes.NewFungibleTokenPacketDataV2( + []*transfertypes.Token{ { Denom: ibctesting.TestCoin.GetDenom(), Amount: ibctesting.TestCoin.Amount.String(), @@ -991,8 +990,8 @@ func (s *CallbacksTestSuite) TestUnmarshalPacketData() { Memo: fmt.Sprintf(`{"src_callback": {"address": "%s"}, "dest_callback": {"address":"%s"}}`, ibctesting.TestAccAddress, ibctesting.TestAccAddress), } - expPacketDataICS20V2 := transferv3types.FungibleTokenPacketData{ - Tokens: []*transferv3types.Token{ + expPacketDataICS20V2 := transfertypes.FungibleTokenPacketDataV2{ + Tokens: []*transfertypes.Token{ { Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), diff --git a/modules/apps/transfer/ibc_module.go b/modules/apps/transfer/ibc_module.go index 7531a9318b5..ddfc4f0a9dd 100644 --- a/modules/apps/transfer/ibc_module.go +++ b/modules/apps/transfer/ibc_module.go @@ -15,7 +15,6 @@ import ( convertinternal "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/convert" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/keeper" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" - v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" host "github.com/cosmos/ibc-go/v8/modules/core/24-host" @@ -175,25 +174,25 @@ func (IBCModule) OnChanCloseConfirm( return nil } -func (IBCModule) unmarshalPacketDataBytesToICS20V2(bz []byte) (v3types.FungibleTokenPacketData, error) { +func (IBCModule) unmarshalPacketDataBytesToICS20V2(bz []byte) (types.FungibleTokenPacketDataV2, error) { // TODO: remove support for this function parsing v1 packet data // TODO: explicit check for packet data type against app version var datav1 types.FungibleTokenPacketData if err := json.Unmarshal(bz, &datav1); err == nil { if len(datav1.Denom) != 0 { - return convertinternal.PacketDataV1ToV3(datav1), nil + return convertinternal.PacketDataV1ToV2(datav1), nil } } - var data v3types.FungibleTokenPacketData + var data types.FungibleTokenPacketDataV2 if err := json.Unmarshal(bz, &data); err == nil { if len(data.Tokens) != 0 { return data, nil } } - return v3types.FungibleTokenPacketData{}, errorsmod.Wrapf(ibcerrors.ErrInvalidType, "cannot unmarshal ICS-20 transfer packet data") + return types.FungibleTokenPacketDataV2{}, errorsmod.Wrapf(ibcerrors.ErrInvalidType, "cannot unmarshal ICS-20 transfer packet data") } // OnRecvPacket implements the IBCModule interface. A successful acknowledgement diff --git a/modules/apps/transfer/ibc_module_test.go b/modules/apps/transfer/ibc_module_test.go index 22b398f4d50..12e6f4c661d 100644 --- a/modules/apps/transfer/ibc_module_test.go +++ b/modules/apps/transfer/ibc_module_test.go @@ -9,7 +9,6 @@ import ( capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" "github.com/cosmos/ibc-go/v8/modules/apps/transfer" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" - v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" @@ -544,8 +543,8 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { { "success: valid packet data multidenom with memo", func() { - initialPacketData = v3types.FungibleTokenPacketData{ - Tokens: []*v3types.Token{ + initialPacketData = types.FungibleTokenPacketDataV2{ + Tokens: []*types.Token{ { Denom: "atom", Amount: ibctesting.TestCoin.Amount.String(), @@ -557,15 +556,15 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { Memo: "some memo", } - data = initialPacketData.(v3types.FungibleTokenPacketData).GetBytes() + data = initialPacketData.(types.FungibleTokenPacketDataV2).GetBytes() }, true, }, { "success: valid packet data multidenom without memo", func() { - initialPacketData = v3types.FungibleTokenPacketData{ - Tokens: []*v3types.Token{ + initialPacketData = types.FungibleTokenPacketDataV2{ + Tokens: []*types.Token{ { Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), @@ -577,7 +576,7 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { Memo: "", } - data = initialPacketData.(v3types.FungibleTokenPacketData).GetBytes() + data = initialPacketData.(types.FungibleTokenPacketDataV2).GetBytes() }, true, }, @@ -600,17 +599,17 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { if tc.expPass { suite.Require().NoError(err) - v3PacketData, ok := packetData.(v3types.FungibleTokenPacketData) + v2PacketData, ok := packetData.(types.FungibleTokenPacketDataV2) suite.Require().True(ok) if v1PacketData, ok := initialPacketData.(types.FungibleTokenPacketData); ok { // Note: testing of the denom trace parsing/conversion should be done as part of testing internal conversion functions - suite.Require().Equal(v1PacketData.Amount, v3PacketData.Tokens[0].Amount) - suite.Require().Equal(v1PacketData.Sender, v3PacketData.Sender) - suite.Require().Equal(v1PacketData.Receiver, v3PacketData.Receiver) - suite.Require().Equal(v1PacketData.Memo, v3PacketData.Memo) + suite.Require().Equal(v1PacketData.Amount, v2PacketData.Tokens[0].Amount) + suite.Require().Equal(v1PacketData.Sender, v2PacketData.Sender) + suite.Require().Equal(v1PacketData.Receiver, v2PacketData.Receiver) + suite.Require().Equal(v1PacketData.Memo, v2PacketData.Memo) } else { - suite.Require().Equal(initialPacketData.(v3types.FungibleTokenPacketData), v3PacketData) + suite.Require().Equal(initialPacketData.(types.FungibleTokenPacketDataV2), v2PacketData) } } else { suite.Require().Error(err) diff --git a/modules/apps/transfer/internal/convert/convert.go b/modules/apps/transfer/internal/convert/convert.go index 2665974d64c..3c39bdeb20b 100644 --- a/modules/apps/transfer/internal/convert/convert.go +++ b/modules/apps/transfer/internal/convert/convert.go @@ -4,18 +4,17 @@ import ( "strings" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" - v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" ) -// PacketDataV1ToV3 converts a v1 (ICS20-V1) packet data to a v3 (ICS20-V2) packet data. -func PacketDataV1ToV3(packetData types.FungibleTokenPacketData) v3types.FungibleTokenPacketData { +// PacketDataV1ToV2 converts a v1 packet data to a v2 packet data. +func PacketDataV1ToV2(packetData types.FungibleTokenPacketData) types.FungibleTokenPacketDataV2 { if err := packetData.ValidateBasic(); err != nil { panic(err) } v2Denom, trace := ExtractDenomAndTraceFromV1Denom(packetData.Denom) - return v3types.FungibleTokenPacketData{ - Tokens: []*v3types.Token{ + return types.FungibleTokenPacketDataV2{ + Tokens: []*types.Token{ { Denom: v2Denom, Amount: packetData.Amount, diff --git a/modules/apps/transfer/internal/convert/convert_test.go b/modules/apps/transfer/internal/convert/convert_test.go index b406210ad47..5860f328e8c 100644 --- a/modules/apps/transfer/internal/convert/convert_test.go +++ b/modules/apps/transfer/internal/convert/convert_test.go @@ -8,10 +8,9 @@ import ( errorsmod "cosmossdk.io/errors" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" - v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" ) -func TestConvertPacketV1ToPacketV3(t *testing.T) { +func TestConvertPacketV1ToPacketV2(t *testing.T) { const ( sender = "sender" receiver = "receiver" @@ -20,14 +19,14 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { testCases := []struct { name string v1Data types.FungibleTokenPacketData - v3Data v3types.FungibleTokenPacketData + v2Data types.FungibleTokenPacketDataV2 expPanic error }{ { "success", types.NewFungibleTokenPacketData("transfer/channel-0/atom", "1000", sender, receiver, ""), - v3types.NewFungibleTokenPacketData( - []*v3types.Token{ + types.NewFungibleTokenPacketDataV2( + []*types.Token{ { Denom: "atom", Amount: "1000", @@ -39,8 +38,8 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { { "success with empty trace", types.NewFungibleTokenPacketData("atom", "1000", sender, receiver, ""), - v3types.NewFungibleTokenPacketData( - []*v3types.Token{ + types.NewFungibleTokenPacketDataV2( + []*types.Token{ { Denom: "atom", Amount: "1000", @@ -52,8 +51,8 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { { "success: base denom with '/'", types.NewFungibleTokenPacketData("transfer/channel-0/atom/withslash", "1000", sender, receiver, ""), - v3types.NewFungibleTokenPacketData( - []*v3types.Token{ + types.NewFungibleTokenPacketDataV2( + []*types.Token{ { Denom: "atom/withslash", Amount: "1000", @@ -65,8 +64,8 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { { "success: base denom with '/' at the end", types.NewFungibleTokenPacketData("transfer/channel-0/atom/", "1000", sender, receiver, ""), - v3types.NewFungibleTokenPacketData( - []*v3types.Token{ + types.NewFungibleTokenPacketDataV2( + []*types.Token{ { Denom: "atom/", Amount: "1000", @@ -78,8 +77,8 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { { "success: longer trace base denom with '/'", types.NewFungibleTokenPacketData("transfer/channel-0/transfer/channel-1/atom/pool", "1000", sender, receiver, ""), - v3types.NewFungibleTokenPacketData( - []*v3types.Token{ + types.NewFungibleTokenPacketDataV2( + []*types.Token{ { Denom: "atom/pool", Amount: "1000", @@ -91,8 +90,8 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { { "success: longer trace with non transfer port", types.NewFungibleTokenPacketData("transfer/channel-0/transfer/channel-1/transfer-custom/channel-2/atom", "1000", sender, receiver, ""), - v3types.NewFungibleTokenPacketData( - []*v3types.Token{ + types.NewFungibleTokenPacketDataV2( + []*types.Token{ { Denom: "atom", Amount: "1000", @@ -104,8 +103,8 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { { "success: base denom with slash, trace with non transfer port", types.NewFungibleTokenPacketData("transfer/channel-0/transfer/channel-1/transfer-custom/channel-2/atom/pool", "1000", sender, receiver, ""), - v3types.NewFungibleTokenPacketData( - []*v3types.Token{ + types.NewFungibleTokenPacketDataV2( + []*types.Token{ { Denom: "atom/pool", Amount: "1000", @@ -117,7 +116,7 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { { "failure: panics with empty denom", types.NewFungibleTokenPacketData("", "1000", sender, receiver, ""), - v3types.FungibleTokenPacketData{}, + types.FungibleTokenPacketDataV2{}, errorsmod.Wrap(types.ErrInvalidDenomForTransfer, "base denomination cannot be blank"), }, } @@ -125,11 +124,11 @@ func TestConvertPacketV1ToPacketV3(t *testing.T) { for _, tc := range testCases { expPass := tc.expPanic == nil if expPass { - v3Data := PacketDataV1ToV3(tc.v1Data) - require.Equal(t, tc.v3Data, v3Data, "test case: %s", tc.name) + actualV2Data := PacketDataV1ToV2(tc.v1Data) + require.Equal(t, tc.v2Data, actualV2Data, "test case: %s", tc.name) } else { require.PanicsWithError(t, tc.expPanic.Error(), func() { - PacketDataV1ToV3(tc.v1Data) + PacketDataV1ToV2(tc.v1Data) }, "test case: %s", tc.name) } } diff --git a/modules/apps/transfer/keeper/mbt_relay_test.go b/modules/apps/transfer/keeper/mbt_relay_test.go index 74c74bc6343..86ad87c3a91 100644 --- a/modules/apps/transfer/keeper/mbt_relay_test.go +++ b/modules/apps/transfer/keeper/mbt_relay_test.go @@ -21,7 +21,6 @@ import ( convertinternal "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/convert" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" - v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" @@ -67,7 +66,7 @@ type FungibleTokenPacket struct { SourcePort string DestChannel string DestPort string - Data v3types.FungibleTokenPacketData + Data types.FungibleTokenPacketDataV2 } type OnRecvPacketTestCase = struct { @@ -151,8 +150,8 @@ func FungibleTokenPacketFromTla(packet TlaFungibleTokenPacket) FungibleTokenPack SourcePort: packet.SourcePort, DestChannel: packet.DestChannel, DestPort: packet.DestPort, - Data: v3types.NewFungibleTokenPacketData( - []*v3types.Token{ + Data: types.NewFungibleTokenPacketDataV2( + []*types.Token{ { Denom: denom, Amount: packet.Data.Amount, diff --git a/modules/apps/transfer/keeper/relay.go b/modules/apps/transfer/keeper/relay.go index fa0e9db7635..bc5c39e5b3a 100644 --- a/modules/apps/transfer/keeper/relay.go +++ b/modules/apps/transfer/keeper/relay.go @@ -14,7 +14,6 @@ import ( convertinternal "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/convert" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" - v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v8/modules/core/24-host" @@ -85,7 +84,7 @@ func (k Keeper) sendTransfer( return 0, errorsmod.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability") } - var tokens []*v3types.Token + var tokens []*types.Token for _, coin := range coins { // NOTE: denomination and hex hash correctness checked during msg.ValidateBasic @@ -136,7 +135,7 @@ func (k Keeper) sendTransfer( } denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(fullDenomPath) - token := &v3types.Token{ + token := &types.Token{ Denom: denom, Amount: coin.Amount.String(), Trace: trace, @@ -144,7 +143,7 @@ func (k Keeper) sendTransfer( tokens = append(tokens, token) } - packetData := v3types.NewFungibleTokenPacketData(tokens, sender.String(), receiver, memo) + packetData := types.NewFungibleTokenPacketDataV2(tokens, sender.String(), receiver, memo) sequence, err := k.ics4Wrapper.SendPacket(ctx, channelCap, sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, packetData.GetBytes()) if err != nil { @@ -178,7 +177,7 @@ func (k Keeper) sendTransfer( // and sent to the receiving address. Otherwise if the sender chain is sending // back tokens this chain originally transferred to it, the tokens are // unescrowed and sent to the receiving address. -func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data v3types.FungibleTokenPacketData) error { +func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketDataV2) error { // validate packet data upon receiving if err := data.ValidateBasic(); err != nil { return errorsmod.Wrapf(err, "error validating ICS-20 transfer packet data") @@ -330,7 +329,7 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data v // acknowledgement written on the receiving chain. If the acknowledgement // was a success then nothing occurs. If the acknowledgement failed, then // the sender is refunded their tokens using the refundPacketToken function. -func (k Keeper) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, data v3types.FungibleTokenPacketData, ack channeltypes.Acknowledgement) error { +func (k Keeper) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketDataV2, ack channeltypes.Acknowledgement) error { switch ack.Response.(type) { case *channeltypes.Acknowledgement_Result: // the acknowledgement succeeded on the receiving chain so nothing @@ -345,7 +344,7 @@ func (k Keeper) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Pac // OnTimeoutPacket refunds the sender since the original packet sent was // never received and has been timed out. -func (k Keeper) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, data v3types.FungibleTokenPacketData) error { +func (k Keeper) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketDataV2) error { return k.refundPacketToken(ctx, packet, data) } @@ -353,7 +352,7 @@ func (k Keeper) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, dat // if the sending chain was the source chain. Otherwise, the sent tokens // were burnt in the original send so new tokens are minted and sent to // the sending address. -func (k Keeper) refundPacketToken(ctx sdk.Context, packet channeltypes.Packet, data v3types.FungibleTokenPacketData) error { +func (k Keeper) refundPacketToken(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketDataV2) error { // NOTE: packet data type already checked in handler.go for _, token := range data.Tokens { diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index 822cae0506a..5ed95db4bab 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -12,7 +12,6 @@ import ( convertinternal "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/convert" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" - v3types "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" ibctesting "github.com/cosmos/ibc-go/v8/testing" @@ -412,8 +411,8 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { tc.malleate() denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) - data := v3types.NewFungibleTokenPacketData( - []*v3types.Token{ + data := types.NewFungibleTokenPacketDataV2( + []*types.Token{ { Denom: denom, Amount: amount.String(), @@ -495,8 +494,8 @@ func (suite *KeeperTestSuite) TestOnRecvPacketSetsTotalEscrowAmountForSourceIBCT } denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) - data := v3types.NewFungibleTokenPacketData( - []*v3types.Token{ + data := types.NewFungibleTokenPacketDataV2( + []*types.Token{ { Denom: denom, Amount: amount.String(), @@ -622,8 +621,8 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { tc.malleate() denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) - data := v3types.NewFungibleTokenPacketData( - []*v3types.Token{ + data := types.NewFungibleTokenPacketDataV2( + []*types.Token{ { Denom: denom, Amount: amount.String(), @@ -715,8 +714,8 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacketSetsTotalEscrowAmountFo ) denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) - data := v3types.NewFungibleTokenPacketData( - []*v3types.Token{ + data := types.NewFungibleTokenPacketDataV2( + []*types.Token{ { Denom: denom, Amount: amount.String(), @@ -838,8 +837,8 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() { tc.malleate() denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) - data := v3types.NewFungibleTokenPacketData( - []*v3types.Token{ + data := types.NewFungibleTokenPacketDataV2( + []*types.Token{ { Denom: denom, Amount: amount.String(), @@ -924,8 +923,8 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacketSetsTotalEscrowAmountForSourceI ) denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) - data := v3types.NewFungibleTokenPacketData( - []*v3types.Token{ + data := types.NewFungibleTokenPacketDataV2( + []*types.Token{ { Denom: denom, Amount: amount.String(), diff --git a/modules/apps/transfer/types/packet.go b/modules/apps/transfer/types/packet.go index 351500abfe1..6ddf67cdbb5 100644 --- a/modules/apps/transfer/types/packet.go +++ b/modules/apps/transfer/types/packet.go @@ -96,3 +96,98 @@ func (ftpd FungibleTokenPacketData) GetCustomPacketData(key string) interface{} return memoData } + +// NewFungibleTokenPacketDataV2 constructs a new NewFungibleTokenPacketDataV2 instance +func NewFungibleTokenPacketDataV2( + tokens []*Token, + sender, receiver string, + memo string, +) FungibleTokenPacketDataV2 { + return FungibleTokenPacketDataV2{ + Tokens: tokens, + Sender: sender, + Receiver: receiver, + Memo: memo, + } +} + +// ValidateBasic is used for validating the token transfer. +// NOTE: The addresses formats are not validated as the sender and recipient can have different +// formats defined by their corresponding chains that are not known to IBC. +func (ftpd FungibleTokenPacketDataV2) ValidateBasic() error { + if strings.TrimSpace(ftpd.Sender) == "" { + return errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "sender address cannot be blank") + } + + if strings.TrimSpace(ftpd.Receiver) == "" { + return errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "receiver address cannot be blank") + } + + if len(ftpd.Tokens) == 0 { + return errorsmod.Wrap(ErrInvalidAmount, "tokens cannot be empty") + } + + for _, token := range ftpd.Tokens { + amount, ok := sdkmath.NewIntFromString(token.Amount) + if !ok { + return errorsmod.Wrapf(ErrInvalidAmount, "unable to parse transfer amount (%s) into math.Int", token.Amount) + } + + if !amount.IsPositive() { + return errorsmod.Wrapf(ErrInvalidAmount, "amount must be strictly positive: got %d", amount) + } + + if err := token.Validate(); err != nil { + return err + } + } + + if len(ftpd.Memo) > MaximumMemoLength { + return errorsmod.Wrapf(ErrInvalidMemo, "memo must not exceed %d bytes", MaximumMemoLength) + } + + return nil +} + +// GetBytes is a helper for serialising +func (ftpd FungibleTokenPacketDataV2) GetBytes() []byte { + bz, err := json.Marshal(&ftpd) + if err != nil { + panic(errors.New("cannot marshal FungibleTokenPacketDataV2 into bytes")) + } + + return bz +} + +// GetCustomPacketData interprets the memo field of the packet data as a JSON object +// and returns the value associated with the given key. +// If the key is missing or the memo is not properly formatted, then nil is returned. +func (ftpd FungibleTokenPacketDataV2) GetCustomPacketData(key string) interface{} { + if len(ftpd.Memo) == 0 { + return nil + } + + jsonObject := make(map[string]interface{}) + err := json.Unmarshal([]byte(ftpd.Memo), &jsonObject) + if err != nil { + return nil + } + + memoData, found := jsonObject[key] + if !found { + return nil + } + + return memoData +} + +// GetPacketSender returns the sender address embedded in the packet data. +// +// NOTE: +// - The sender address is set by the module which requested the packet to be sent, +// and this module may not have validated the sender address by a signature check. +// - The sender address must only be used by modules on the sending chain. +// - sourcePortID is not used in this implementation. +func (ftpd FungibleTokenPacketDataV2) GetPacketSender(sourcePortID string) string { + return ftpd.Sender +} diff --git a/modules/apps/transfer/types/packet.pb.go b/modules/apps/transfer/types/packet.pb.go index 5b0c659e69c..020e16b93ef 100644 --- a/modules/apps/transfer/types/packet.pb.go +++ b/modules/apps/transfer/types/packet.pb.go @@ -106,8 +106,149 @@ func (m *FungibleTokenPacketData) GetMemo() string { return "" } +// FungibleTokenPacketDataV2 defines a struct for the packet payload +// See FungibleTokenPacketDataV2 spec: +// https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#data-structures +type FungibleTokenPacketDataV2 struct { + // the tokens to be transferred + Tokens []*Token `protobuf:"bytes,1,rep,name=tokens,proto3" json:"tokens,omitempty"` + // the sender address + Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty"` + // the recipient address on the destination chain + Receiver string `protobuf:"bytes,3,opt,name=receiver,proto3" json:"receiver,omitempty"` + // optional memo + Memo string `protobuf:"bytes,4,opt,name=memo,proto3" json:"memo,omitempty"` +} + +func (m *FungibleTokenPacketDataV2) Reset() { *m = FungibleTokenPacketDataV2{} } +func (m *FungibleTokenPacketDataV2) String() string { return proto.CompactTextString(m) } +func (*FungibleTokenPacketDataV2) ProtoMessage() {} +func (*FungibleTokenPacketDataV2) Descriptor() ([]byte, []int) { + return fileDescriptor_653ca2ce9a5ca313, []int{1} +} +func (m *FungibleTokenPacketDataV2) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FungibleTokenPacketDataV2) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FungibleTokenPacketDataV2.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 *FungibleTokenPacketDataV2) XXX_Merge(src proto.Message) { + xxx_messageInfo_FungibleTokenPacketDataV2.Merge(m, src) +} +func (m *FungibleTokenPacketDataV2) XXX_Size() int { + return m.Size() +} +func (m *FungibleTokenPacketDataV2) XXX_DiscardUnknown() { + xxx_messageInfo_FungibleTokenPacketDataV2.DiscardUnknown(m) +} + +var xxx_messageInfo_FungibleTokenPacketDataV2 proto.InternalMessageInfo + +func (m *FungibleTokenPacketDataV2) GetTokens() []*Token { + if m != nil { + return m.Tokens + } + return nil +} + +func (m *FungibleTokenPacketDataV2) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *FungibleTokenPacketDataV2) GetReceiver() string { + if m != nil { + return m.Receiver + } + return "" +} + +func (m *FungibleTokenPacketDataV2) GetMemo() string { + if m != nil { + return m.Memo + } + return "" +} + +// Token defines a struct which represents a token to be transferred. +type Token struct { + // the base token denomination to be transferred + Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` + // the token amount to be transferred + Amount string `protobuf:"bytes,2,opt,name=amount,proto3" json:"amount,omitempty"` + // the trace of the token + Trace []string `protobuf:"bytes,3,rep,name=trace,proto3" json:"trace,omitempty"` +} + +func (m *Token) Reset() { *m = Token{} } +func (m *Token) String() string { return proto.CompactTextString(m) } +func (*Token) ProtoMessage() {} +func (*Token) Descriptor() ([]byte, []int) { + return fileDescriptor_653ca2ce9a5ca313, []int{2} +} +func (m *Token) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Token) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Token.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 *Token) XXX_Merge(src proto.Message) { + xxx_messageInfo_Token.Merge(m, src) +} +func (m *Token) XXX_Size() int { + return m.Size() +} +func (m *Token) XXX_DiscardUnknown() { + xxx_messageInfo_Token.DiscardUnknown(m) +} + +var xxx_messageInfo_Token proto.InternalMessageInfo + +func (m *Token) GetDenom() string { + if m != nil { + return m.Denom + } + return "" +} + +func (m *Token) GetAmount() string { + if m != nil { + return m.Amount + } + return "" +} + +func (m *Token) GetTrace() []string { + if m != nil { + return m.Trace + } + return nil +} + func init() { proto.RegisterType((*FungibleTokenPacketData)(nil), "ibc.applications.transfer.v2.FungibleTokenPacketData") + proto.RegisterType((*FungibleTokenPacketDataV2)(nil), "ibc.applications.transfer.v2.FungibleTokenPacketDataV2") + proto.RegisterType((*Token)(nil), "ibc.applications.transfer.v2.Token") } func init() { @@ -115,23 +256,28 @@ func init() { } var fileDescriptor_653ca2ce9a5ca313 = []byte{ - // 254 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x90, 0xb1, 0x4a, 0x34, 0x31, - 0x14, 0x46, 0x27, 0xff, 0xbf, 0xbb, 0x68, 0xca, 0x20, 0x3a, 0x88, 0x04, 0xb1, 0xd2, 0xc2, 0x09, - 0xac, 0x85, 0xd6, 0x22, 0xd6, 0x2a, 0x56, 0x76, 0x49, 0xe6, 0x3a, 0x86, 0x9d, 0xe4, 0x86, 0x24, - 0x33, 0xe0, 0x53, 0xe8, 0x63, 0x59, 0x6e, 0x69, 0x29, 0x33, 0x2f, 0x22, 0x9b, 0x51, 0xd9, 0x2e, - 0xe7, 0xe4, 0xbb, 0xcd, 0xa1, 0x67, 0x46, 0x69, 0x21, 0xbd, 0x6f, 0x8d, 0x96, 0xc9, 0xa0, 0x8b, - 0x22, 0x05, 0xe9, 0xe2, 0x33, 0x04, 0xd1, 0x2f, 0x85, 0x97, 0x7a, 0x05, 0xa9, 0xf2, 0x01, 0x13, - 0xb2, 0x23, 0xa3, 0x74, 0xb5, 0x3d, 0xad, 0x7e, 0xa7, 0x55, 0xbf, 0x3c, 0x79, 0x23, 0xf4, 0xe0, - 0xb6, 0x73, 0x8d, 0x51, 0x2d, 0x3c, 0xe2, 0x0a, 0xdc, 0x5d, 0xbe, 0xbd, 0x91, 0x49, 0xb2, 0x3d, - 0x3a, 0xaf, 0xc1, 0xa1, 0x2d, 0xc9, 0x31, 0x39, 0xdd, 0x7d, 0x98, 0x80, 0xed, 0xd3, 0x85, 0xb4, - 0xd8, 0xb9, 0x54, 0xfe, 0xcb, 0xfa, 0x87, 0x36, 0x3e, 0x82, 0xab, 0x21, 0x94, 0xff, 0x27, 0x3f, - 0x11, 0x3b, 0xa4, 0x3b, 0x01, 0x34, 0x98, 0x1e, 0x42, 0x39, 0xcb, 0x3f, 0x7f, 0xcc, 0x18, 0x9d, - 0x59, 0xb0, 0x58, 0xce, 0xb3, 0xcf, 0xef, 0xeb, 0xfb, 0x8f, 0x81, 0x93, 0xf5, 0xc0, 0xc9, 0xd7, - 0xc0, 0xc9, 0xfb, 0xc8, 0x8b, 0xf5, 0xc8, 0x8b, 0xcf, 0x91, 0x17, 0x4f, 0x97, 0x8d, 0x49, 0x2f, - 0x9d, 0xaa, 0x34, 0x5a, 0xa1, 0x31, 0x5a, 0x8c, 0xc2, 0x28, 0x7d, 0xde, 0xa0, 0xe8, 0xaf, 0x84, - 0xc5, 0xba, 0x6b, 0x21, 0x6e, 0xa2, 0x6c, 0xc5, 0x48, 0xaf, 0x1e, 0xa2, 0x5a, 0xe4, 0x12, 0x17, - 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x93, 0x3d, 0xc6, 0x36, 0x36, 0x01, 0x00, 0x00, + // 325 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0x31, 0x4b, 0x03, 0x31, + 0x14, 0xc7, 0x9b, 0x5e, 0xaf, 0x68, 0xdc, 0x8e, 0xa2, 0x51, 0xe4, 0x28, 0x75, 0xa9, 0x83, 0x09, + 0x9c, 0x83, 0x82, 0x9b, 0x88, 0x8b, 0x8b, 0x16, 0x71, 0x70, 0xcb, 0xa5, 0xcf, 0x1a, 0xda, 0x24, + 0x47, 0x92, 0x3b, 0xf0, 0x53, 0xe8, 0x47, 0xf0, 0xe3, 0x38, 0x76, 0x74, 0x94, 0xf6, 0x8b, 0xc8, + 0xa5, 0x55, 0x6e, 0xa9, 0xe0, 0x96, 0xff, 0x3f, 0xff, 0xf7, 0xf8, 0x25, 0xef, 0xe1, 0x63, 0x99, + 0x0b, 0xc6, 0x8b, 0x62, 0x26, 0x05, 0xf7, 0xd2, 0x68, 0xc7, 0xbc, 0xe5, 0xda, 0x3d, 0x81, 0x65, + 0x55, 0xc6, 0x0a, 0x2e, 0xa6, 0xe0, 0x69, 0x61, 0x8d, 0x37, 0xc9, 0xa1, 0xcc, 0x05, 0x6d, 0x46, + 0xe9, 0x4f, 0x94, 0x56, 0xd9, 0xe0, 0x15, 0xe1, 0xbd, 0xeb, 0x52, 0x4f, 0x64, 0x3e, 0x83, 0x7b, + 0x33, 0x05, 0x7d, 0x1b, 0x6a, 0xaf, 0xb8, 0xe7, 0x49, 0x0f, 0xc7, 0x63, 0xd0, 0x46, 0x11, 0xd4, + 0x47, 0xc3, 0xed, 0xd1, 0x4a, 0x24, 0xbb, 0xb8, 0xcb, 0x95, 0x29, 0xb5, 0x27, 0xed, 0x60, 0xaf, + 0x55, 0xed, 0x3b, 0xd0, 0x63, 0xb0, 0x24, 0x5a, 0xf9, 0x2b, 0x95, 0x1c, 0xe0, 0x2d, 0x0b, 0x02, + 0x64, 0x05, 0x96, 0x74, 0xc2, 0xcd, 0xaf, 0x4e, 0x12, 0xdc, 0x51, 0xa0, 0x0c, 0x89, 0x83, 0x1f, + 0xce, 0x83, 0x77, 0x84, 0xf7, 0x37, 0x10, 0x3d, 0x64, 0xc9, 0x05, 0xee, 0xfa, 0xda, 0x74, 0x04, + 0xf5, 0xa3, 0xe1, 0x4e, 0x76, 0x44, 0xff, 0x7a, 0x1e, 0x0d, 0x0d, 0x46, 0xeb, 0x92, 0x06, 0x62, + 0x7b, 0x23, 0x62, 0xb4, 0x01, 0xb1, 0xd3, 0x40, 0xbc, 0xc1, 0x71, 0x68, 0xfc, 0xcf, 0x1f, 0xea, + 0xe1, 0xd8, 0x5b, 0x2e, 0x80, 0x44, 0xfd, 0xa8, 0x4e, 0x07, 0x71, 0x79, 0xf7, 0xb1, 0x48, 0xd1, + 0x7c, 0x91, 0xa2, 0xaf, 0x45, 0x8a, 0xde, 0x96, 0x69, 0x6b, 0xbe, 0x4c, 0x5b, 0x9f, 0xcb, 0xb4, + 0xf5, 0x78, 0x36, 0x91, 0xfe, 0xb9, 0xcc, 0xa9, 0x30, 0x8a, 0x09, 0xe3, 0x94, 0x71, 0x4c, 0xe6, + 0xe2, 0x64, 0x62, 0x58, 0x75, 0xce, 0x94, 0x19, 0x97, 0x33, 0x70, 0xf5, 0x12, 0x34, 0x86, 0xef, + 0x5f, 0x0a, 0x70, 0x79, 0x37, 0x4c, 0xfe, 0xf4, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x65, 0x55, 0xb8, + 0x16, 0x26, 0x02, 0x00, 0x00, } func (m *FungibleTokenPacketData) Marshal() (dAtA []byte, err error) { @@ -192,6 +338,110 @@ func (m *FungibleTokenPacketData) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } +func (m *FungibleTokenPacketDataV2) 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 *FungibleTokenPacketDataV2) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FungibleTokenPacketDataV2) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Memo) > 0 { + i -= len(m.Memo) + copy(dAtA[i:], m.Memo) + i = encodeVarintPacket(dAtA, i, uint64(len(m.Memo))) + i-- + dAtA[i] = 0x22 + } + if len(m.Receiver) > 0 { + i -= len(m.Receiver) + copy(dAtA[i:], m.Receiver) + i = encodeVarintPacket(dAtA, i, uint64(len(m.Receiver))) + i-- + dAtA[i] = 0x1a + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintPacket(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0x12 + } + if len(m.Tokens) > 0 { + for iNdEx := len(m.Tokens) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Tokens[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPacket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Token) 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 *Token) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Token) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Trace) > 0 { + for iNdEx := len(m.Trace) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Trace[iNdEx]) + copy(dAtA[i:], m.Trace[iNdEx]) + i = encodeVarintPacket(dAtA, i, uint64(len(m.Trace[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Amount) > 0 { + i -= len(m.Amount) + copy(dAtA[i:], m.Amount) + i = encodeVarintPacket(dAtA, i, uint64(len(m.Amount))) + i-- + dAtA[i] = 0x12 + } + if len(m.Denom) > 0 { + i -= len(m.Denom) + copy(dAtA[i:], m.Denom) + i = encodeVarintPacket(dAtA, i, uint64(len(m.Denom))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintPacket(dAtA []byte, offset int, v uint64) int { offset -= sovPacket(v) base := offset @@ -232,6 +482,56 @@ func (m *FungibleTokenPacketData) Size() (n int) { return n } +func (m *FungibleTokenPacketDataV2) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Tokens) > 0 { + for _, e := range m.Tokens { + l = e.Size() + n += 1 + l + sovPacket(uint64(l)) + } + } + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovPacket(uint64(l)) + } + l = len(m.Receiver) + if l > 0 { + n += 1 + l + sovPacket(uint64(l)) + } + l = len(m.Memo) + if l > 0 { + n += 1 + l + sovPacket(uint64(l)) + } + return n +} + +func (m *Token) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Denom) + if l > 0 { + n += 1 + l + sovPacket(uint64(l)) + } + l = len(m.Amount) + if l > 0 { + n += 1 + l + sovPacket(uint64(l)) + } + if len(m.Trace) > 0 { + for _, s := range m.Trace { + l = len(s) + n += 1 + l + sovPacket(uint64(l)) + } + } + return n +} + func sovPacket(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -448,6 +748,332 @@ func (m *FungibleTokenPacketData) Unmarshal(dAtA []byte) error { } return nil } +func (m *FungibleTokenPacketDataV2) 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 ErrIntOverflowPacket + } + 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: FungibleTokenPacketDataV2: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FungibleTokenPacketDataV2: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tokens", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPacket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Tokens = append(m.Tokens, &Token{}) + if err := m.Tokens[len(m.Tokens)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + 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 ErrInvalidLengthPacket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Receiver", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + 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 ErrInvalidLengthPacket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Receiver = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Memo", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + 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 ErrInvalidLengthPacket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Memo = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPacket(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPacket + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Token) 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 ErrIntOverflowPacket + } + 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: Token: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Token: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + 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 ErrInvalidLengthPacket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + 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 ErrInvalidLengthPacket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Amount = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Trace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + 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 ErrInvalidLengthPacket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Trace = append(m.Trace, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPacket(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPacket + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipPacket(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/modules/apps/transfer/types/packet_test.go b/modules/apps/transfer/types/packet_test.go index d06d0774c7d..759253ccfec 100644 --- a/modules/apps/transfer/types/packet_test.go +++ b/modules/apps/transfer/types/packet_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" ) const ( @@ -159,3 +160,411 @@ func (suite *TypesTestSuite) TestFungibleTokenPacketDataOmitEmpty() { // check that the memo field is present in the marshalled bytes suite.Require().Contains(string(bz), "memo") } + +// TestFungibleTokenPacketDataValidateBasic tests ValidateBasic for FungibleTokenPacketData +func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { + testCases := []struct { + name string + packetData types.FungibleTokenPacketDataV2 + expErr error + }{ + { + "success: valid packet", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "", + ), + nil, + }, + { + "success: valid packet with memo", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "memo", + ), + nil, + }, + { + "success: valid packet with large amount", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: largeAmount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "memo", + ), + nil, + }, + { + "failure: invalid denom", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: "", + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "", + ), + types.ErrInvalidDenomForTransfer, + }, + { + "failure: invalid empty amount", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: "", + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "", + ), + types.ErrInvalidAmount, + }, + { + "failure: invalid empty token array", + types.NewFungibleTokenPacketDataV2( + []*types.Token{}, + sender, + receiver, + "", + ), + types.ErrInvalidAmount, + }, + { + "failure: invalid zero amount", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: "0", + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "", + ), + types.ErrInvalidAmount, + }, + { + "failure: invalid negative amount", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: "-100", + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "", + ), + types.ErrInvalidAmount, + }, + { + "failure: invalid large amount", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: invalidLargeAmount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "memo", + ), + types.ErrInvalidAmount, + }, + { + "failure: missing sender address", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + "", + receiver, + "memo", + ), + ibcerrors.ErrInvalidAddress, + }, + { + "failure: missing recipient address", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + "", + "", + ), + ibcerrors.ErrInvalidAddress, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tc.packetData.ValidateBasic() + + expPass := tc.expErr == nil + if expPass { + require.NoError(t, err, tc.name) + } else { + require.ErrorContains(t, err, tc.expErr.Error(), tc.name) + } + }) + } +} + +func TestGetPacketSender(t *testing.T) { + testCases := []struct { + name string + packetData types.FungibleTokenPacketDataV2 + expSender string + }{ + { + "non-empty sender field", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "", + ), + sender, + }, + { + "empty sender field", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + "", + receiver, + "abc", + ), + "", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + require.Equal(t, tc.expSender, tc.packetData.GetPacketSender(types.PortID)) + }) + } +} + +func TestPacketDataProvider(t *testing.T) { + testCases := []struct { + name string + packetData types.FungibleTokenPacketDataV2 + expCustomData interface{} + }{ + { + "success: src_callback key in memo", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + fmt.Sprintf(`{"src_callback": {"address": "%s"}}`, receiver)), + + map[string]interface{}{ + "address": receiver, + }, + }, + { + "success: src_callback key in memo with additional fields", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + fmt.Sprintf(`{"src_callback": {"address": "%s", "gas_limit": "200000"}}`, receiver)), + map[string]interface{}{ + "address": receiver, + "gas_limit": "200000", + }, + }, + { + "success: src_callback has string value", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + `{"src_callback": "string"}`), + "string", + }, + { + "failure: src_callback key not found memo", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + fmt.Sprintf(`{"dest_callback": {"address": "%s", "min_gas": "200000"}}`, receiver)), + nil, + }, + { + "failure: empty memo", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + ""), + nil, + }, + { + "failure: non-json memo", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "invalid"), + nil, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + customData := tc.packetData.GetCustomPacketData("src_callback") + require.Equal(t, tc.expCustomData, customData) + }) + } +} + +func TestFungibleTokenPacketDataOmitEmpty(t *testing.T) { + testCases := []struct { + name string + packetData types.FungibleTokenPacketDataV2 + expMemo bool + }{ + { + "empty memo field, resulting marshalled bytes should not contain the memo field", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "", + ), + false, + }, + { + "non-empty memo field, resulting marshalled bytes should contain the memo field", + types.NewFungibleTokenPacketDataV2( + []*types.Token{ + { + Denom: denom, + Amount: amount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + "abc", + ), + true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + bz, err := json.Marshal(tc.packetData) + if tc.expMemo { + require.NoError(t, err, tc.name) + // check that the memo field is present in the marshalled bytes + require.Contains(t, string(bz), "memo") + } else { + require.NoError(t, err, tc.name) + // check that the memo field is not present in the marshalled bytes + require.NotContains(t, string(bz), "memo") + } + }) + } +} diff --git a/modules/apps/transfer/types/v3/token.go b/modules/apps/transfer/types/token.go similarity index 79% rename from modules/apps/transfer/types/v3/token.go rename to modules/apps/transfer/types/token.go index 8eea4eab756..bdfb7880577 100644 --- a/modules/apps/transfer/types/v3/token.go +++ b/modules/apps/transfer/types/token.go @@ -1,4 +1,4 @@ -package v3 +package types import ( "strings" @@ -8,13 +8,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" denominternal "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/denom" - "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" ) -// ValidateToken validates a token denomination and trace identifiers. +// Validate validates a token denomination and trace identifiers. func (t Token) Validate() error { if err := sdk.ValidateDenom(t.Denom); err != nil { - return errorsmod.Wrap(types.ErrInvalidDenomForTransfer, err.Error()) + return errorsmod.Wrap(ErrInvalidDenomForTransfer, err.Error()) } if len(t.Trace) == 0 { diff --git a/modules/apps/transfer/types/v3/token_test.go b/modules/apps/transfer/types/token_test.go similarity index 83% rename from modules/apps/transfer/types/v3/token_test.go rename to modules/apps/transfer/types/token_test.go index 13654cfcc1d..efbee444478 100644 --- a/modules/apps/transfer/types/v3/token_test.go +++ b/modules/apps/transfer/types/token_test.go @@ -1,4 +1,4 @@ -package v3 +package types import ( fmt "fmt" @@ -6,18 +6,29 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + denom = "atom/pool" + amount = "100" +) + +var ( + sender = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String() + receiver = sdk.AccAddress("testaddr2").String() ) func TestGetFullDenomPath(t *testing.T) { testCases := []struct { name string - packetData FungibleTokenPacketData + packetData FungibleTokenPacketDataV2 expPath string }{ { "denom path with trace", - NewFungibleTokenPacketData( + NewFungibleTokenPacketDataV2( []*Token{ { Denom: denom, @@ -33,7 +44,7 @@ func TestGetFullDenomPath(t *testing.T) { }, { "nil trace", - NewFungibleTokenPacketData( + NewFungibleTokenPacketDataV2( []*Token{ { Denom: denom, @@ -49,7 +60,7 @@ func TestGetFullDenomPath(t *testing.T) { }, { "empty string trace", - NewFungibleTokenPacketData( + NewFungibleTokenPacketDataV2( []*Token{ { Denom: denom, @@ -113,7 +124,7 @@ func TestValidate(t *testing.T) { Amount: amount, Trace: nil, }, - types.ErrInvalidDenomForTransfer, + ErrInvalidDenomForTransfer, }, { "failure: invalid identifier in trace", diff --git a/modules/apps/transfer/types/v3/packet.go b/modules/apps/transfer/types/v3/packet.go deleted file mode 100644 index 510a1024cac..00000000000 --- a/modules/apps/transfer/types/v3/packet.go +++ /dev/null @@ -1,114 +0,0 @@ -package v3 - -import ( - "encoding/json" - "errors" - "strings" - - errorsmod "cosmossdk.io/errors" - sdkmath "cosmossdk.io/math" - - "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" - ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" - ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" -) - -var ( - _ ibcexported.PacketData = (*FungibleTokenPacketData)(nil) - _ ibcexported.PacketDataProvider = (*FungibleTokenPacketData)(nil) -) - -// NewFungibleTokenPacketData constructs a new NewFungibleTokenPacketData instance -func NewFungibleTokenPacketData( - tokens []*Token, - sender, receiver string, - memo string, -) FungibleTokenPacketData { - return FungibleTokenPacketData{ - Tokens: tokens, - Sender: sender, - Receiver: receiver, - Memo: memo, - } -} - -// ValidateBasic is used for validating the token transfer. -// NOTE: The addresses formats are not validated as the sender and recipient can have different -// formats defined by their corresponding chains that are not known to IBC. -func (ftpd FungibleTokenPacketData) ValidateBasic() error { - if strings.TrimSpace(ftpd.Sender) == "" { - return errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "sender address cannot be blank") - } - - if strings.TrimSpace(ftpd.Receiver) == "" { - return errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "receiver address cannot be blank") - } - - if len(ftpd.Tokens) == 0 { - return errorsmod.Wrap(types.ErrInvalidAmount, "tokens cannot be empty") - } - - for _, token := range ftpd.Tokens { - amount, ok := sdkmath.NewIntFromString(token.Amount) - if !ok { - return errorsmod.Wrapf(types.ErrInvalidAmount, "unable to parse transfer amount (%s) into math.Int", token.Amount) - } - - if !amount.IsPositive() { - return errorsmod.Wrapf(types.ErrInvalidAmount, "amount must be strictly positive: got %d", amount) - } - - if err := token.Validate(); err != nil { - return err - } - } - - if len(ftpd.Memo) > types.MaximumMemoLength { - return errorsmod.Wrapf(types.ErrInvalidMemo, "memo must not exceed %d bytes", types.MaximumMemoLength) - } - - return nil -} - -// GetBytes is a helper for serialising -func (ftpd FungibleTokenPacketData) GetBytes() []byte { - bz, err := json.Marshal(&ftpd) - if err != nil { - panic(errors.New("cannot marshal v3 FungibleTokenPacketData into bytes")) - } - - return bz -} - -// GetCustomPacketData interprets the memo field of the packet data as a JSON object -// and returns the value associated with the given key. -// If the key is missing or the memo is not properly formatted, then nil is returned. -func (ftpd FungibleTokenPacketData) GetCustomPacketData(key string) interface{} { - if len(ftpd.Memo) == 0 { - return nil - } - - jsonObject := make(map[string]interface{}) - err := json.Unmarshal([]byte(ftpd.Memo), &jsonObject) - if err != nil { - return nil - } - - memoData, found := jsonObject[key] - if !found { - return nil - } - - return memoData -} - -// GetPacketSender returns the sender address embedded in the packet data. -// -// NOTE: -// - The sender address is set by the module which requested the packet to be sent, -// and this module may not have validated the sender address by a signature check. -// - The sender address must only be used by modules on the sending chain. -// - sourcePortID is not used in this implementation. -func (ftpd FungibleTokenPacketData) GetPacketSender(sourcePortID string) string { - return ftpd.Sender -} diff --git a/modules/apps/transfer/types/v3/packet.pb.go b/modules/apps/transfer/types/v3/packet.pb.go deleted file mode 100644 index 145b51c5eb9..00000000000 --- a/modules/apps/transfer/types/v3/packet.pb.go +++ /dev/null @@ -1,776 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: ibc/applications/transfer/v3/packet.proto - -package v3 - -import ( - fmt "fmt" - proto "github.com/cosmos/gogoproto/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 - -// FungibleTokenPacketData defines a struct for the packet payload -// See FungibleTokenPacketData spec: -// https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#data-structures -type FungibleTokenPacketData struct { - // the tokens to be transferred - Tokens []*Token `protobuf:"bytes,1,rep,name=tokens,proto3" json:"tokens,omitempty"` - // the sender address - Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty"` - // the recipient address on the destination chain - Receiver string `protobuf:"bytes,3,opt,name=receiver,proto3" json:"receiver,omitempty"` - // optional memo - Memo string `protobuf:"bytes,4,opt,name=memo,proto3" json:"memo,omitempty"` -} - -func (m *FungibleTokenPacketData) Reset() { *m = FungibleTokenPacketData{} } -func (m *FungibleTokenPacketData) String() string { return proto.CompactTextString(m) } -func (*FungibleTokenPacketData) ProtoMessage() {} -func (*FungibleTokenPacketData) Descriptor() ([]byte, []int) { - return fileDescriptor_760742a8894acdbe, []int{0} -} -func (m *FungibleTokenPacketData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *FungibleTokenPacketData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_FungibleTokenPacketData.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 *FungibleTokenPacketData) XXX_Merge(src proto.Message) { - xxx_messageInfo_FungibleTokenPacketData.Merge(m, src) -} -func (m *FungibleTokenPacketData) XXX_Size() int { - return m.Size() -} -func (m *FungibleTokenPacketData) XXX_DiscardUnknown() { - xxx_messageInfo_FungibleTokenPacketData.DiscardUnknown(m) -} - -var xxx_messageInfo_FungibleTokenPacketData proto.InternalMessageInfo - -func (m *FungibleTokenPacketData) GetTokens() []*Token { - if m != nil { - return m.Tokens - } - return nil -} - -func (m *FungibleTokenPacketData) GetSender() string { - if m != nil { - return m.Sender - } - return "" -} - -func (m *FungibleTokenPacketData) GetReceiver() string { - if m != nil { - return m.Receiver - } - return "" -} - -func (m *FungibleTokenPacketData) GetMemo() string { - if m != nil { - return m.Memo - } - return "" -} - -// Token defines a struct which represents a token to be transferred. -type Token struct { - // the base token denomination to be transferred - Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` - // the token amount to be transferred - Amount string `protobuf:"bytes,2,opt,name=amount,proto3" json:"amount,omitempty"` - // the trace of the token - Trace []string `protobuf:"bytes,3,rep,name=trace,proto3" json:"trace,omitempty"` -} - -func (m *Token) Reset() { *m = Token{} } -func (m *Token) String() string { return proto.CompactTextString(m) } -func (*Token) ProtoMessage() {} -func (*Token) Descriptor() ([]byte, []int) { - return fileDescriptor_760742a8894acdbe, []int{1} -} -func (m *Token) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *Token) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_Token.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 *Token) XXX_Merge(src proto.Message) { - xxx_messageInfo_Token.Merge(m, src) -} -func (m *Token) XXX_Size() int { - return m.Size() -} -func (m *Token) XXX_DiscardUnknown() { - xxx_messageInfo_Token.DiscardUnknown(m) -} - -var xxx_messageInfo_Token proto.InternalMessageInfo - -func (m *Token) GetDenom() string { - if m != nil { - return m.Denom - } - return "" -} - -func (m *Token) GetAmount() string { - if m != nil { - return m.Amount - } - return "" -} - -func (m *Token) GetTrace() []string { - if m != nil { - return m.Trace - } - return nil -} - -func init() { - proto.RegisterType((*FungibleTokenPacketData)(nil), "ibc.applications.transfer.v3.FungibleTokenPacketData") - proto.RegisterType((*Token)(nil), "ibc.applications.transfer.v3.Token") -} - -func init() { - proto.RegisterFile("ibc/applications/transfer/v3/packet.proto", fileDescriptor_760742a8894acdbe) -} - -var fileDescriptor_760742a8894acdbe = []byte{ - // 301 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x90, 0xc1, 0x4a, 0x33, 0x31, - 0x14, 0x85, 0x9b, 0x7f, 0xda, 0xf2, 0x1b, 0x77, 0x41, 0x74, 0x10, 0x19, 0x4a, 0xdd, 0xd4, 0x85, - 0x09, 0x38, 0x1b, 0xd1, 0x9d, 0x88, 0x1b, 0x37, 0x52, 0xba, 0x72, 0x97, 0xa4, 0xd7, 0x1a, 0xda, - 0xe4, 0x0e, 0x49, 0x66, 0xc0, 0xb7, 0xf0, 0x09, 0x7c, 0x1e, 0x97, 0x5d, 0xba, 0x94, 0xf6, 0x45, - 0x64, 0xd2, 0x56, 0xba, 0x72, 0x77, 0xbf, 0x7b, 0xcf, 0xb9, 0x07, 0x0e, 0xbd, 0x30, 0x4a, 0x0b, - 0x59, 0x55, 0x0b, 0xa3, 0x65, 0x34, 0xe8, 0x82, 0x88, 0x5e, 0xba, 0xf0, 0x02, 0x5e, 0x34, 0xa5, - 0xa8, 0xa4, 0x9e, 0x43, 0xe4, 0x95, 0xc7, 0x88, 0xec, 0xcc, 0x28, 0xcd, 0xf7, 0xa5, 0x7c, 0x27, - 0xe5, 0x4d, 0x39, 0xfc, 0x20, 0xf4, 0xe4, 0xa1, 0x76, 0x33, 0xa3, 0x16, 0x30, 0xc1, 0x39, 0xb8, - 0xa7, 0xe4, 0xbd, 0x97, 0x51, 0xb2, 0x5b, 0xda, 0x8f, 0xed, 0x2a, 0xe4, 0x64, 0x90, 0x8d, 0x0e, - 0xaf, 0xce, 0xf9, 0x5f, 0xaf, 0x78, 0xb2, 0x8f, 0xb7, 0x16, 0x76, 0x4c, 0xfb, 0x01, 0xdc, 0x14, - 0x7c, 0xfe, 0x6f, 0x40, 0x46, 0x07, 0xe3, 0x2d, 0xb1, 0x53, 0xfa, 0xdf, 0x83, 0x06, 0xd3, 0x80, - 0xcf, 0xb3, 0x74, 0xf9, 0x65, 0xc6, 0x68, 0xd7, 0x82, 0xc5, 0xbc, 0x9b, 0xf6, 0x69, 0x1e, 0x3e, - 0xd2, 0x5e, 0x7a, 0xcc, 0x8e, 0x68, 0x6f, 0x0a, 0x0e, 0x6d, 0x4e, 0xd2, 0x75, 0x03, 0x6d, 0x8c, - 0xb4, 0x58, 0xbb, 0xb8, 0x8b, 0xd9, 0x50, 0xab, 0x8e, 0x5e, 0x6a, 0xc8, 0xb3, 0x41, 0xd6, 0xaa, - 0x13, 0xdc, 0x4d, 0x3e, 0x57, 0x05, 0x59, 0xae, 0x0a, 0xf2, 0xbd, 0x2a, 0xc8, 0xfb, 0xba, 0xe8, - 0x2c, 0xd7, 0x45, 0xe7, 0x6b, 0x5d, 0x74, 0x9e, 0x6f, 0x66, 0x26, 0xbe, 0xd6, 0x8a, 0x6b, 0xb4, - 0x42, 0x63, 0xb0, 0x18, 0x84, 0x51, 0xfa, 0x72, 0x86, 0xa2, 0xb9, 0x16, 0x16, 0xa7, 0xf5, 0x02, - 0x42, 0x5b, 0xf8, 0x5e, 0xd1, 0xf1, 0xad, 0x82, 0x20, 0x9a, 0x52, 0xf5, 0x53, 0xd1, 0xe5, 0x4f, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x97, 0x4d, 0xcc, 0xde, 0x95, 0x01, 0x00, 0x00, -} - -func (m *FungibleTokenPacketData) 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 *FungibleTokenPacketData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *FungibleTokenPacketData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Memo) > 0 { - i -= len(m.Memo) - copy(dAtA[i:], m.Memo) - i = encodeVarintPacket(dAtA, i, uint64(len(m.Memo))) - i-- - dAtA[i] = 0x22 - } - if len(m.Receiver) > 0 { - i -= len(m.Receiver) - copy(dAtA[i:], m.Receiver) - i = encodeVarintPacket(dAtA, i, uint64(len(m.Receiver))) - i-- - dAtA[i] = 0x1a - } - if len(m.Sender) > 0 { - i -= len(m.Sender) - copy(dAtA[i:], m.Sender) - i = encodeVarintPacket(dAtA, i, uint64(len(m.Sender))) - i-- - dAtA[i] = 0x12 - } - if len(m.Tokens) > 0 { - for iNdEx := len(m.Tokens) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Tokens[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintPacket(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - } - return len(dAtA) - i, nil -} - -func (m *Token) 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 *Token) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *Token) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Trace) > 0 { - for iNdEx := len(m.Trace) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.Trace[iNdEx]) - copy(dAtA[i:], m.Trace[iNdEx]) - i = encodeVarintPacket(dAtA, i, uint64(len(m.Trace[iNdEx]))) - i-- - dAtA[i] = 0x1a - } - } - if len(m.Amount) > 0 { - i -= len(m.Amount) - copy(dAtA[i:], m.Amount) - i = encodeVarintPacket(dAtA, i, uint64(len(m.Amount))) - i-- - dAtA[i] = 0x12 - } - if len(m.Denom) > 0 { - i -= len(m.Denom) - copy(dAtA[i:], m.Denom) - i = encodeVarintPacket(dAtA, i, uint64(len(m.Denom))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func encodeVarintPacket(dAtA []byte, offset int, v uint64) int { - offset -= sovPacket(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *FungibleTokenPacketData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Tokens) > 0 { - for _, e := range m.Tokens { - l = e.Size() - n += 1 + l + sovPacket(uint64(l)) - } - } - l = len(m.Sender) - if l > 0 { - n += 1 + l + sovPacket(uint64(l)) - } - l = len(m.Receiver) - if l > 0 { - n += 1 + l + sovPacket(uint64(l)) - } - l = len(m.Memo) - if l > 0 { - n += 1 + l + sovPacket(uint64(l)) - } - return n -} - -func (m *Token) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Denom) - if l > 0 { - n += 1 + l + sovPacket(uint64(l)) - } - l = len(m.Amount) - if l > 0 { - n += 1 + l + sovPacket(uint64(l)) - } - if len(m.Trace) > 0 { - for _, s := range m.Trace { - l = len(s) - n += 1 + l + sovPacket(uint64(l)) - } - } - return n -} - -func sovPacket(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozPacket(x uint64) (n int) { - return sovPacket(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *FungibleTokenPacketData) 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 ErrIntOverflowPacket - } - 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: FungibleTokenPacketData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: FungibleTokenPacketData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Tokens", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacket - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthPacket - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthPacket - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Tokens = append(m.Tokens, &Token{}) - if err := m.Tokens[len(m.Tokens)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacket - } - 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 ErrInvalidLengthPacket - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthPacket - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Sender = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Receiver", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacket - } - 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 ErrInvalidLengthPacket - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthPacket - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Receiver = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Memo", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacket - } - 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 ErrInvalidLengthPacket - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthPacket - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Memo = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipPacket(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthPacket - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *Token) 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 ErrIntOverflowPacket - } - 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: Token: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Token: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacket - } - 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 ErrInvalidLengthPacket - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthPacket - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Denom = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacket - } - 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 ErrInvalidLengthPacket - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthPacket - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Amount = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Trace", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacket - } - 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 ErrInvalidLengthPacket - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthPacket - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Trace = append(m.Trace, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipPacket(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthPacket - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipPacket(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, ErrIntOverflowPacket - } - 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, ErrIntOverflowPacket - } - 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, ErrIntOverflowPacket - } - 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, ErrInvalidLengthPacket - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupPacket - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthPacket - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLengthPacket = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowPacket = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupPacket = fmt.Errorf("proto: unexpected end of group") -) diff --git a/modules/apps/transfer/types/v3/packet_test.go b/modules/apps/transfer/types/v3/packet_test.go deleted file mode 100644 index 63435d8589e..00000000000 --- a/modules/apps/transfer/types/v3/packet_test.go +++ /dev/null @@ -1,436 +0,0 @@ -package v3 - -import ( - "encoding/json" - "fmt" - "testing" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/cometbft/cometbft/crypto/secp256k1" - - "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" - ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" -) - -const ( - denom = "atom/pool" - amount = "1000" - largeAmount = "18446744073709551616" // one greater than largest uint64 (^uint64(0)) - invalidLargeAmount = "115792089237316195423570985008687907853269984665640564039457584007913129639936" // 2^256 -) - -var ( - sender = secp256k1.GenPrivKey().PubKey().Address().String() - receiver = sdk.AccAddress("testaddr2").String() -) - -// TestFungibleTokenPacketDataValidateBasic tests ValidateBasic for FungibleTokenPacketData -func TestFungibleTokenPacketDataValidateBasic(t *testing.T) { - testCases := []struct { - name string - packetData FungibleTokenPacketData - expErr error - }{ - { - "success: valid packet", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: amount, - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - receiver, - "", - ), - nil, - }, - { - "success: valid packet with memo", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: amount, - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - receiver, - "memo", - ), - nil, - }, - { - "success: valid packet with large amount", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: largeAmount, - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - receiver, - "memo", - ), - nil, - }, - { - "failure: invalid denom", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: "", - Amount: amount, - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - receiver, - "", - ), - types.ErrInvalidDenomForTransfer, - }, - { - "failure: invalid empty amount", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: "", - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - receiver, - "", - ), - types.ErrInvalidAmount, - }, - { - "failure: invalid empty token array", - NewFungibleTokenPacketData( - []*Token{}, - sender, - receiver, - "", - ), - types.ErrInvalidAmount, - }, - { - "failure: invalid zero amount", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: "0", - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - receiver, - "", - ), - types.ErrInvalidAmount, - }, - { - "failure: invalid negative amount", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: "-100", - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - receiver, - "", - ), - types.ErrInvalidAmount, - }, - { - "failure: invalid large amount", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: invalidLargeAmount, - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - receiver, - "memo", - ), - types.ErrInvalidAmount, - }, - { - "failure: missing sender address", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: amount, - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - "", - receiver, - "memo", - ), - ibcerrors.ErrInvalidAddress, - }, - { - "failure: missing recipient address", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: amount, - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - "", - "", - ), - ibcerrors.ErrInvalidAddress, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - err := tc.packetData.ValidateBasic() - - expPass := tc.expErr == nil - if expPass { - require.NoError(t, err, tc.name) - } else { - require.ErrorContains(t, err, tc.expErr.Error(), tc.name) - } - }) - } -} - -func TestGetPacketSender(t *testing.T) { - testCases := []struct { - name string - packetData FungibleTokenPacketData - expSender string - }{ - { - "non-empty sender field", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: amount, - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - receiver, - "", - ), - sender, - }, - { - "empty sender field", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: amount, - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - "", - receiver, - "abc", - ), - "", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - require.Equal(t, tc.expSender, tc.packetData.GetPacketSender(types.PortID)) - }) - } -} - -func TestPacketDataProvider(t *testing.T) { - testCases := []struct { - name string - packetData FungibleTokenPacketData - expCustomData interface{} - }{ - { - "success: src_callback key in memo", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: amount, - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - receiver, - fmt.Sprintf(`{"src_callback": {"address": "%s"}}`, receiver)), - - map[string]interface{}{ - "address": receiver, - }, - }, - { - "success: src_callback key in memo with additional fields", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: amount, - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - receiver, - fmt.Sprintf(`{"src_callback": {"address": "%s", "gas_limit": "200000"}}`, receiver)), - map[string]interface{}{ - "address": receiver, - "gas_limit": "200000", - }, - }, - { - "success: src_callback has string value", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: amount, - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - receiver, - `{"src_callback": "string"}`), - "string", - }, - { - "failure: src_callback key not found memo", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: amount, - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - receiver, - fmt.Sprintf(`{"dest_callback": {"address": "%s", "min_gas": "200000"}}`, receiver)), - nil, - }, - { - "failure: empty memo", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: amount, - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - receiver, - ""), - nil, - }, - { - "failure: non-json memo", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: amount, - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - receiver, - "invalid"), - nil, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - customData := tc.packetData.GetCustomPacketData("src_callback") - require.Equal(t, tc.expCustomData, customData) - }) - } -} - -func TestFungibleTokenPacketDataOmitEmpty(t *testing.T) { - testCases := []struct { - name string - packetData FungibleTokenPacketData - expMemo bool - }{ - { - "empty memo field, resulting marshalled bytes should not contain the memo field", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: amount, - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - receiver, - "", - ), - false, - }, - { - "non-empty memo field, resulting marshalled bytes should contain the memo field", - NewFungibleTokenPacketData( - []*Token{ - { - Denom: denom, - Amount: amount, - Trace: []string{"transfer/channel-0", "transfer/channel-1"}, - }, - }, - sender, - receiver, - "abc", - ), - true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - bz, err := json.Marshal(tc.packetData) - if tc.expMemo { - require.NoError(t, err, tc.name) - // check that the memo field is present in the marshalled bytes - require.Contains(t, string(bz), "memo") - } else { - require.NoError(t, err, tc.name) - // check that the memo field is not present in the marshalled bytes - require.NotContains(t, string(bz), "memo") - } - }) - } -} diff --git a/proto/ibc/applications/transfer/v2/packet.proto b/proto/ibc/applications/transfer/v2/packet.proto index bff35bdd6d3..4dfaf107062 100644 --- a/proto/ibc/applications/transfer/v2/packet.proto +++ b/proto/ibc/applications/transfer/v2/packet.proto @@ -19,3 +19,27 @@ message FungibleTokenPacketData { // optional memo string memo = 5; } + +// FungibleTokenPacketDataV2 defines a struct for the packet payload +// See FungibleTokenPacketDataV2 spec: +// https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#data-structures +message FungibleTokenPacketDataV2 { + // the tokens to be transferred + repeated Token tokens = 1; + // the sender address + string sender = 2; + // the recipient address on the destination chain + string receiver = 3; + // optional memo + string memo = 4; +} + +// Token defines a struct which represents a token to be transferred. +message Token { + // the base token denomination to be transferred + string denom = 1; + // the token amount to be transferred + string amount = 2; + // the trace of the token + repeated string trace = 3; +} diff --git a/proto/ibc/applications/transfer/v3/packet.proto b/proto/ibc/applications/transfer/v3/packet.proto deleted file mode 100644 index 8971472c69d..00000000000 --- a/proto/ibc/applications/transfer/v3/packet.proto +++ /dev/null @@ -1,29 +0,0 @@ -syntax = "proto3"; - -package ibc.applications.transfer.v3; - -option go_package = "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types/v3"; - -// FungibleTokenPacketData defines a struct for the packet payload -// See FungibleTokenPacketData spec: -// https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#data-structures -message FungibleTokenPacketData { - // the tokens to be transferred - repeated Token tokens = 1; - // the sender address - string sender = 2; - // the recipient address on the destination chain - string receiver = 3; - // optional memo - string memo = 4; -} - -// Token defines a struct which represents a token to be transferred. -message Token { - // the base token denomination to be transferred - string denom = 1; - // the token amount to be transferred - string amount = 2; - // the trace of the token - repeated string trace = 3; -} From 9b399449acafda46655d4ac8802f888b24fd7047 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Tue, 21 May 2024 14:00:35 +0100 Subject: [PATCH 18/42] Adding additional comments and changing version variable names (#6345) --- e2e/tests/transfer/localhost_test.go | 8 +++--- e2e/tests/transfer/upgrades_test.go | 4 +-- e2e/testsuite/testsuite.go | 6 ++-- modules/apps/29-fee/keeper/events_test.go | 2 +- modules/apps/29-fee/transfer_test.go | 8 +++--- modules/apps/callbacks/callbacks_test.go | 6 ++-- modules/apps/transfer/ibc_module.go | 6 ++-- modules/apps/transfer/ibc_module_test.go | 28 +++++++++---------- .../apps/transfer/keeper/mbt_relay_test.go | 2 +- modules/apps/transfer/keeper/msg_server.go | 2 +- .../apps/transfer/keeper/msg_server_test.go | 2 +- modules/apps/transfer/types/keys.go | 16 ++++++----- testing/path.go | 4 +-- testing/solomachine.go | 10 +++---- 14 files changed, 53 insertions(+), 51 deletions(-) diff --git a/e2e/tests/transfer/localhost_test.go b/e2e/tests/transfer/localhost_test.go index a8909fa9675..246fa201553 100644 --- a/e2e/tests/transfer/localhost_test.go +++ b/e2e/tests/transfer/localhost_test.go @@ -68,7 +68,7 @@ func (s *LocalhostTransferTestSuite) TestMsgTransfer_Localhost() { t.Run("channel open init localhost", func(t *testing.T) { msgChanOpenInit := channeltypes.NewMsgChannelOpenInit( - transfertypes.PortID, transfertypes.Version, + transfertypes.PortID, transfertypes.V2, channeltypes.UNORDERED, []string{exported.LocalhostConnectionID}, transfertypes.PortID, rlyWallet.FormattedAddress(), ) @@ -83,10 +83,10 @@ func (s *LocalhostTransferTestSuite) TestMsgTransfer_Localhost() { t.Run("channel open try localhost", func(t *testing.T) { msgChanOpenTry := channeltypes.NewMsgChannelOpenTry( - transfertypes.PortID, transfertypes.Version, + transfertypes.PortID, transfertypes.V2, channeltypes.UNORDERED, []string{exported.LocalhostConnectionID}, transfertypes.PortID, msgChanOpenInitRes.ChannelId, - transfertypes.Version, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), + transfertypes.V2, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), ) txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenTry) @@ -98,7 +98,7 @@ func (s *LocalhostTransferTestSuite) TestMsgTransfer_Localhost() { t.Run("channel open ack localhost", func(t *testing.T) { msgChanOpenAck := channeltypes.NewMsgChannelOpenAck( transfertypes.PortID, msgChanOpenInitRes.ChannelId, - msgChanOpenTryRes.ChannelId, transfertypes.Version, + msgChanOpenTryRes.ChannelId, transfertypes.V2, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(), ) diff --git a/e2e/tests/transfer/upgrades_test.go b/e2e/tests/transfer/upgrades_test.go index 74f5593d611..11a2d1a84e4 100644 --- a/e2e/tests/transfer/upgrades_test.go +++ b/e2e/tests/transfer/upgrades_test.go @@ -361,7 +361,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_ s.Require().NoError(err) s.Require().Equal(channeltypes.OPEN, channel.State, "the channel state is not OPEN") - s.Require().Equal(transfertypes.Version, channel.Version, "the channel version is not ics20-1") + s.Require().Equal(transfertypes.V2, channel.Version, "the channel version is not ics20-2") errorReceipt, err := s.QueryUpgradeError(ctx, chainA, channelA.PortID, channelA.ChannelID) s.Require().NoError(err) @@ -374,7 +374,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_ s.Require().NoError(err) s.Require().Equal(channeltypes.OPEN, channel.State, "the channel state is not OPEN") - s.Require().Equal(transfertypes.Version, channel.Version, "the channel version is not ics20-1") + s.Require().Equal(transfertypes.V2, channel.Version, "the channel version is not ics20-2") errorReceipt, err := s.QueryUpgradeError(ctx, chainB, channelB.PortID, channelB.ChannelID) s.Require().NoError(err) diff --git a/e2e/testsuite/testsuite.go b/e2e/testsuite/testsuite.go index dc188180aa1..990cbf06fc1 100644 --- a/e2e/testsuite/testsuite.go +++ b/e2e/testsuite/testsuite.go @@ -111,7 +111,7 @@ func (s *E2ETestSuite) ConfigureRelayer(ctx context.Context, chainA, chainB ibc. channelOptions := ibc.DefaultChannelOpts() // TODO: better way to do this. // For now, set the version to the latest transfer module version - channelOptions.Version = transfertypes.Version + channelOptions.Version = transfertypes.V2 if channelOpts != nil { channelOpts(&channelOptions) @@ -401,7 +401,7 @@ func (s *E2ETestSuite) GetRelayerExecReporter() *testreporter.RelayerExecReporte // TransferChannelOptions configures both of the chains to have non-incentivized transfer channels. func (*E2ETestSuite) TransferChannelOptions() func(options *ibc.CreateChannelOptions) { return func(opts *ibc.CreateChannelOptions) { - opts.Version = transfertypes.Version + opts.Version = transfertypes.V2 opts.SourcePortName = transfertypes.PortID opts.DestPortName = transfertypes.PortID } @@ -411,7 +411,7 @@ func (*E2ETestSuite) TransferChannelOptions() func(options *ibc.CreateChannelOpt func (s *E2ETestSuite) FeeMiddlewareChannelOptions() func(options *ibc.CreateChannelOptions) { versionMetadata := feetypes.Metadata{ FeeVersion: feetypes.Version, - AppVersion: transfertypes.Version, + AppVersion: transfertypes.V2, } versionBytes, err := feetypes.ModuleCdc.MarshalJSON(&versionMetadata) s.Require().NoError(err) diff --git a/modules/apps/29-fee/keeper/events_test.go b/modules/apps/29-fee/keeper/events_test.go index d70b8bfd229..38b64cadd80 100644 --- a/modules/apps/29-fee/keeper/events_test.go +++ b/modules/apps/29-fee/keeper/events_test.go @@ -93,7 +93,7 @@ func (suite *KeeperTestSuite) TestIncentivizePacketEvent() { func (suite *KeeperTestSuite) TestDistributeFeeEvent() { // create an incentivized transfer path path := ibctesting.NewPath(suite.chainA, suite.chainB) - feeTransferVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: transfertypes.Version})) + feeTransferVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: transfertypes.V2})) path.EndpointA.ChannelConfig.Version = feeTransferVersion path.EndpointB.ChannelConfig.Version = feeTransferVersion path.EndpointA.ChannelConfig.PortID = transfertypes.PortID diff --git a/modules/apps/29-fee/transfer_test.go b/modules/apps/29-fee/transfer_test.go index f76393bfcf8..11145598d4b 100644 --- a/modules/apps/29-fee/transfer_test.go +++ b/modules/apps/29-fee/transfer_test.go @@ -12,7 +12,7 @@ import ( // Integration test to ensure ics29 works with ics20 func (suite *FeeTestSuite) TestFeeTransfer() { path := ibctesting.NewPath(suite.chainA, suite.chainB) - feeTransferVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: transfertypes.Version})) + feeTransferVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: transfertypes.V2})) path.EndpointA.ChannelConfig.Version = feeTransferVersion path.EndpointB.ChannelConfig.Version = feeTransferVersion path.EndpointA.ChannelConfig.PortID = transfertypes.PortID @@ -94,13 +94,13 @@ func (suite *FeeTestSuite) TestTransferFeeUpgrade() { // configure the initial path to create a regular transfer channel path.EndpointA.ChannelConfig.PortID = transfertypes.PortID path.EndpointB.ChannelConfig.PortID = transfertypes.PortID - path.EndpointA.ChannelConfig.Version = transfertypes.Version - path.EndpointB.ChannelConfig.Version = transfertypes.Version + path.EndpointA.ChannelConfig.Version = transfertypes.V2 + path.EndpointB.ChannelConfig.Version = transfertypes.V2 path.Setup() // configure the channel upgrade to upgrade to an incentivized fee enabled transfer channel - upgradeVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: transfertypes.Version})) + upgradeVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: transfertypes.V2})) path.EndpointA.ChannelConfig.ProposedUpgrade.Fields.Version = upgradeVersion path.EndpointB.ChannelConfig.ProposedUpgrade.Fields.Version = upgradeVersion diff --git a/modules/apps/callbacks/callbacks_test.go b/modules/apps/callbacks/callbacks_test.go index 3b337b7bd24..6c76deb5795 100644 --- a/modules/apps/callbacks/callbacks_test.go +++ b/modules/apps/callbacks/callbacks_test.go @@ -78,8 +78,8 @@ func (s *CallbacksTestSuite) SetupTransferTest() { s.path.EndpointA.ChannelConfig.PortID = ibctesting.TransferPort s.path.EndpointB.ChannelConfig.PortID = ibctesting.TransferPort - s.path.EndpointA.ChannelConfig.Version = transfertypes.Version - s.path.EndpointB.ChannelConfig.Version = transfertypes.Version + s.path.EndpointA.ChannelConfig.Version = transfertypes.V2 + s.path.EndpointB.ChannelConfig.Version = transfertypes.V2 s.path.Setup() } @@ -88,7 +88,7 @@ func (s *CallbacksTestSuite) SetupTransferTest() { func (s *CallbacksTestSuite) SetupFeeTransferTest() { s.setupChains() - feeTransferVersion := string(feetypes.ModuleCdc.MustMarshalJSON(&feetypes.Metadata{FeeVersion: feetypes.Version, AppVersion: transfertypes.Version})) + feeTransferVersion := string(feetypes.ModuleCdc.MustMarshalJSON(&feetypes.Metadata{FeeVersion: feetypes.Version, AppVersion: transfertypes.V2})) s.path.EndpointA.ChannelConfig.Version = feeTransferVersion s.path.EndpointB.ChannelConfig.Version = feeTransferVersion s.path.EndpointA.ChannelConfig.PortID = transfertypes.PortID diff --git a/modules/apps/transfer/ibc_module.go b/modules/apps/transfer/ibc_module.go index ddfc4f0a9dd..90cc379258d 100644 --- a/modules/apps/transfer/ibc_module.go +++ b/modules/apps/transfer/ibc_module.go @@ -89,7 +89,7 @@ func (im IBCModule) OnChanOpenInit( // default to latest supported version if strings.TrimSpace(version) == "" { - version = types.Version + version = types.V2 } if !slices.Contains(types.SupportedVersions, version) { @@ -120,7 +120,7 @@ func (im IBCModule) OnChanOpenTry( } if !slices.Contains(types.SupportedVersions, counterpartyVersion) { - return types.Version, nil + return types.V2, nil } // OpenTry must claim the channelCapability that IBC passes into the callback @@ -375,7 +375,7 @@ func (im IBCModule) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, } if !slices.Contains(types.SupportedVersions, counterpartyVersion) { - return types.Version, nil + return types.V2, nil } return counterpartyVersion, nil diff --git a/modules/apps/transfer/ibc_module_test.go b/modules/apps/transfer/ibc_module_test.go index 12e6f4c661d..2fd36dbe422 100644 --- a/modules/apps/transfer/ibc_module_test.go +++ b/modules/apps/transfer/ibc_module_test.go @@ -31,24 +31,24 @@ func (suite *TransferTestSuite) TestOnChanOpenInit() { expVersion string }{ { - "success", func() {}, true, types.Version, + "success", func() {}, true, types.V2, }, { // connection hops is not used in the transfer application callback, // it is already validated in the core OnChanUpgradeInit. "success: invalid connection hops", func() { path.EndpointA.ConnectionID = "invalid-connection-id" - }, true, types.Version, + }, true, types.V2, }, { "success: empty version string", func() { channel.Version = "" - }, true, types.Version, + }, true, types.V2, }, { "success: ics20-1 legacy", func() { - channel.Version = types.Version1 - }, true, types.Version1, + channel.Version = types.V1 + }, true, types.V1, }, { "max channels reached", func() { @@ -93,7 +93,7 @@ func (suite *TransferTestSuite) TestOnChanOpenInit() { Ordering: channeltypes.UNORDERED, Counterparty: counterparty, ConnectionHops: []string{path.EndpointA.ConnectionID}, - Version: types.Version, + Version: types.V2, } var err error @@ -134,17 +134,17 @@ func (suite *TransferTestSuite) TestOnChanOpenTry() { expVersion string }{ { - "success", func() {}, true, types.Version, + "success", func() {}, true, types.V2, }, { "success: counterparty version is legacy ics20-1", func() { - counterpartyVersion = types.Version1 - }, true, types.Version1, + counterpartyVersion = types.V1 + }, true, types.V1, }, { "success: invalid counterparty version, we use our proposed version", func() { counterpartyVersion = "version" - }, true, types.Version, + }, true, types.V2, }, { "max channels reached", func() { @@ -185,9 +185,9 @@ func (suite *TransferTestSuite) TestOnChanOpenTry() { Ordering: channeltypes.UNORDERED, Counterparty: counterparty, ConnectionHops: []string{path.EndpointA.ConnectionID}, - Version: types.Version, + Version: types.V2, } - counterpartyVersion = types.Version + counterpartyVersion = types.V2 module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.TransferPort) suite.Require().NoError(err) @@ -242,7 +242,7 @@ func (suite *TransferTestSuite) TestOnChanOpenAck() { path := ibctesting.NewTransferPath(suite.chainA, suite.chainB) path.SetupConnections() path.EndpointA.ChannelID = ibctesting.FirstChannelID - counterpartyVersion = types.Version + counterpartyVersion = types.V2 module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.TransferPort) suite.Require().NoError(err) @@ -405,7 +405,7 @@ func (suite *TransferTestSuite) TestOnChanUpgradeTry() { expPass := tc.expError == nil if expPass { suite.Require().NoError(err) - suite.Require().Equal(types.Version, version) + suite.Require().Equal(types.V2, version) } else { suite.Require().Error(err) suite.Require().Contains(err.Error(), tc.expError.Error()) diff --git a/modules/apps/transfer/keeper/mbt_relay_test.go b/modules/apps/transfer/keeper/mbt_relay_test.go index 86ad87c3a91..6c0752e6b5f 100644 --- a/modules/apps/transfer/keeper/mbt_relay_test.go +++ b/modules/apps/transfer/keeper/mbt_relay_test.go @@ -109,7 +109,7 @@ func AddressFromTla(addr []string) string { s = addr[2] } else if len(addr[2]) == 0 { // escrow address: ics20-1\x00port/channel - s = fmt.Sprintf("%s\x00%s/%s", types.Version1, addr[0], addr[1]) + s = fmt.Sprintf("%s\x00%s/%s", types.V1, addr[0], addr[1]) } else { panic(errors.New("failed to convert from TLA+ address: neither simple nor escrow address")) } diff --git a/modules/apps/transfer/keeper/msg_server.go b/modules/apps/transfer/keeper/msg_server.go index aa81a9a228a..74bb202f521 100644 --- a/modules/apps/transfer/keeper/msg_server.go +++ b/modules/apps/transfer/keeper/msg_server.go @@ -36,7 +36,7 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types. } // ics20-1 only supports a single token, so if that is the current version, we must only process a single token. - if appVersion == types.Version1 && len(tokens) > 1 { + if appVersion == types.V1 && len(tokens) > 1 { return nil, errorsmod.Wrapf(ibcerrors.ErrInvalidRequest, "cannot transfer multiple tokens with ics20-1") } diff --git a/modules/apps/transfer/keeper/msg_server_test.go b/modules/apps/transfer/keeper/msg_server_test.go index 5ab8a03e069..ae4aaef3107 100644 --- a/modules/apps/transfer/keeper/msg_server_test.go +++ b/modules/apps/transfer/keeper/msg_server_test.go @@ -143,7 +143,7 @@ func (suite *KeeperTestSuite) TestMsgTransfer() { // explicitly set to ics20-1 which does not support multi-denom path.EndpointA.UpdateChannel(func(channel *channeltypes.Channel) { - channel.Version = types.Version1 + channel.Version = types.V1 }) }, ibcerrors.ErrInvalidRequest, diff --git a/modules/apps/transfer/types/keys.go b/modules/apps/transfer/types/keys.go index e44d521856a..3321243ae84 100644 --- a/modules/apps/transfer/types/keys.go +++ b/modules/apps/transfer/types/keys.go @@ -35,15 +35,17 @@ const ( ) const ( - // Version defines the current version the IBC transfer - // module supports - Version = "ics20-2" + // V1 defines first version of the IBC transfer module + V1 = "ics20-1" - // Version1 defines first version of the IBC transfer module - Version1 = "ics20-1" + // V2 defines the current version the IBC transfer + // module supports + V2 = "ics20-2" // escrowAddressVersion should remain as ics20-1 to avoid the address changing. - escrowAddressVersion = Version1 + // this address has been reasoned about to avoid collisions with other addresses + // https://github.com/cosmos/cosmos-sdk/issues/7737#issuecomment-735671951 + escrowAddressVersion = V1 ) var ( @@ -53,7 +55,7 @@ var ( DenomTraceKey = []byte{0x02} // SupportedVersions defines all versions that are supported by the module - SupportedVersions = []string{Version, Version1} + SupportedVersions = []string{V2, V1} ) // GetEscrowAddress returns the escrow address for the specified channel. diff --git a/testing/path.go b/testing/path.go index cb1c968952a..0a101c4739e 100644 --- a/testing/path.go +++ b/testing/path.go @@ -47,8 +47,8 @@ func NewTransferPath(chainA, chainB *TestChain) *Path { path := NewPath(chainA, chainB) path.EndpointA.ChannelConfig.PortID = TransferPort path.EndpointB.ChannelConfig.PortID = TransferPort - path.EndpointA.ChannelConfig.Version = transfertypes.Version - path.EndpointB.ChannelConfig.Version = transfertypes.Version + path.EndpointA.ChannelConfig.Version = transfertypes.V2 + path.EndpointB.ChannelConfig.Version = transfertypes.V2 return path } diff --git a/testing/solomachine.go b/testing/solomachine.go index 46151bec3ea..2a10fc50107 100644 --- a/testing/solomachine.go +++ b/testing/solomachine.go @@ -312,7 +312,7 @@ func (solo *Solomachine) ConnOpenAck(chain *TestChain, clientID, connectionID st func (solo *Solomachine) ChanOpenInit(chain *TestChain, connectionID string) string { msgChanOpenInit := channeltypes.NewMsgChannelOpenInit( transfertypes.PortID, - transfertypes.Version, + transfertypes.V2, channeltypes.UNORDERED, []string{connectionID}, transfertypes.PortID, @@ -332,12 +332,12 @@ func (solo *Solomachine) ChanOpenInit(chain *TestChain, connectionID string) str // ChanOpenAck performs the channel open ack handshake step on the tendermint chain for the associated // solo machine client. func (solo *Solomachine) ChanOpenAck(chain *TestChain, channelID string) { - tryProof := solo.GenerateChanOpenTryProof(transfertypes.PortID, transfertypes.Version, channelID) + tryProof := solo.GenerateChanOpenTryProof(transfertypes.PortID, transfertypes.V2, channelID) msgChanOpenAck := channeltypes.NewMsgChannelOpenAck( transfertypes.PortID, channelID, channelIDSolomachine, - transfertypes.Version, + transfertypes.V2, tryProof, clienttypes.ZeroHeight(), chain.SenderAccount.GetAddress().String(), @@ -351,7 +351,7 @@ func (solo *Solomachine) ChanOpenAck(chain *TestChain, channelID string) { // ChanCloseConfirm performs the channel close confirm handshake step on the tendermint chain for the associated // solo machine client. func (solo *Solomachine) ChanCloseConfirm(chain *TestChain, portID, channelID string) { - initProof := solo.GenerateChanClosedProof(portID, transfertypes.Version, channelID) + initProof := solo.GenerateChanClosedProof(portID, transfertypes.V2, channelID) msgChanCloseConfirm := channeltypes.NewMsgChannelCloseConfirm( portID, channelID, @@ -442,7 +442,7 @@ func (solo *Solomachine) TimeoutPacket(chain *TestChain, packet channeltypes.Pac // TimeoutPacketOnClose creates a channel closed and unreceived packet proof and broadcasts a MsgTimeoutOnClose. func (solo *Solomachine) TimeoutPacketOnClose(chain *TestChain, packet channeltypes.Packet, channelID string) { - closedProof := solo.GenerateChanClosedProof(transfertypes.PortID, transfertypes.Version, channelID) + closedProof := solo.GenerateChanClosedProof(transfertypes.PortID, transfertypes.V2, channelID) unreceivedProof := solo.GenerateReceiptAbsenceProof(packet) msgTimeout := channeltypes.NewMsgTimeoutOnClose( packet, From 7897ef360da23cd8026dbbc6b61c288566fa431d Mon Sep 17 00:00:00 2001 From: chatton Date: Tue, 21 May 2024 15:41:08 +0100 Subject: [PATCH 19/42] chore: correctly claim capability --- modules/apps/29-fee/transfer_test.go | 31 ++++++++++++++-------------- modules/apps/transfer/ibc_module.go | 8 +++---- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/modules/apps/29-fee/transfer_test.go b/modules/apps/29-fee/transfer_test.go index 9f8492dcdb1..dcfa344ab87 100644 --- a/modules/apps/29-fee/transfer_test.go +++ b/modules/apps/29-fee/transfer_test.go @@ -164,19 +164,18 @@ func (suite *FeeTestSuite) TestTransferFeeUpgrade() { } } -// TODO: fix this test -//func (suite *FeeTestSuite) TestOnesidedFeeMiddlewareTransferHandshake() { -// RemoveFeeMiddleware(suite.chainB) // remove fee middleware from chainB -// -// path := ibctesting.NewPath(suite.chainA, suite.chainB) -// feeTransferVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: transfertypes.V1})) -// path.EndpointA.ChannelConfig.Version = feeTransferVersion // this will be renegotiated by the Try step -// path.EndpointB.ChannelConfig.Version = "" // this will be overwritten by the Try step -// path.EndpointA.ChannelConfig.PortID = transfertypes.PortID -// path.EndpointB.ChannelConfig.PortID = transfertypes.PortID -// -// path.Setup() -// -// suite.Require().Equal(path.EndpointA.ChannelConfig.Version, transfertypes.V1) -// suite.Require().Equal(path.EndpointB.ChannelConfig.Version, transfertypes.V1) -//} +func (suite *FeeTestSuite) TestOnesidedFeeMiddlewareTransferHandshake() { + RemoveFeeMiddleware(suite.chainB) // remove fee middleware from chainB + + path := ibctesting.NewPath(suite.chainA, suite.chainB) + feeTransferVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: transfertypes.V2})) + path.EndpointA.ChannelConfig.Version = feeTransferVersion // this will be renegotiated by the Try step + path.EndpointB.ChannelConfig.Version = "" // this will be overwritten by the Try step + path.EndpointA.ChannelConfig.PortID = transfertypes.PortID + path.EndpointB.ChannelConfig.PortID = transfertypes.PortID + + path.Setup() + + suite.Require().Equal(path.EndpointA.ChannelConfig.Version, transfertypes.V2) + suite.Require().Equal(path.EndpointB.ChannelConfig.Version, transfertypes.V2) +} diff --git a/modules/apps/transfer/ibc_module.go b/modules/apps/transfer/ibc_module.go index 52e75eb51b7..62a5b975506 100644 --- a/modules/apps/transfer/ibc_module.go +++ b/modules/apps/transfer/ibc_module.go @@ -119,15 +119,15 @@ func (im IBCModule) OnChanOpenTry( return "", err } - if !slices.Contains(types.SupportedVersions, counterpartyVersion) { - return types.V2, nil - } - // OpenTry must claim the channelCapability that IBC passes into the callback if err := im.keeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { return "", err } + if !slices.Contains(types.SupportedVersions, counterpartyVersion) { + return types.V2, nil + } + return counterpartyVersion, nil } From 786a4f1c86acc3bad5afeff26bf3bdf1976a9240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Colin=20Axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Tue, 21 May 2024 17:20:33 +0200 Subject: [PATCH 20/42] lint --- modules/apps/transfer/types/token_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/apps/transfer/types/token_test.go b/modules/apps/transfer/types/token_test.go index efbee444478..deb468e7066 100644 --- a/modules/apps/transfer/types/token_test.go +++ b/modules/apps/transfer/types/token_test.go @@ -1,7 +1,7 @@ package types import ( - fmt "fmt" + "fmt" "testing" "github.com/stretchr/testify/require" From a84b0e713c56e15e977f5e446a036c524be6df0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Wed, 22 May 2024 10:38:16 +0200 Subject: [PATCH 21/42] imp: change ics20 events to emit token set (#6348) * refactor: quick change to tokens event attribute * fix: various tests * lint * lint:fixeroni * imp: remove events variable in favour of direct event emission --------- Co-authored-by: DimitrisJim --- modules/apps/callbacks/ibc_middleware_test.go | 10 +-- modules/apps/transfer/ibc_module.go | 86 ++++++++----------- modules/apps/transfer/ibc_module_test.go | 4 +- .../apps/transfer/internal/convert/convert.go | 2 +- .../transfer/internal/convert/convert_test.go | 14 +-- .../apps/transfer/keeper/mbt_relay_test.go | 2 +- modules/apps/transfer/keeper/msg_server.go | 24 +++--- .../apps/transfer/keeper/msg_server_test.go | 13 +-- modules/apps/transfer/keeper/relay.go | 4 +- modules/apps/transfer/keeper/relay_test.go | 12 +-- modules/apps/transfer/types/events.go | 2 +- modules/apps/transfer/types/msgs.go | 2 +- modules/apps/transfer/types/packet.go | 2 +- modules/apps/transfer/types/packet.pb.go | 52 +++++------ modules/apps/transfer/types/packet_test.go | 42 ++++----- modules/apps/transfer/types/token.go | 22 +++++ modules/apps/transfer/types/token_test.go | 6 +- .../ibc/applications/transfer/v2/packet.proto | 4 +- 18 files changed, 151 insertions(+), 152 deletions(-) diff --git a/modules/apps/callbacks/ibc_middleware_test.go b/modules/apps/callbacks/ibc_middleware_test.go index 553f2fd4631..fa8ee5794ab 100644 --- a/modules/apps/callbacks/ibc_middleware_test.go +++ b/modules/apps/callbacks/ibc_middleware_test.go @@ -165,7 +165,7 @@ func (s *CallbacksTestSuite) TestSendPacket() { transferICS4Wrapper := GetSimApp(s.chainA).TransferKeeper.GetICS4Wrapper() packetData = transfertypes.NewFungibleTokenPacketDataV2( - []*transfertypes.Token{ + []transfertypes.Token{ { Denom: ibctesting.TestCoin.GetDenom(), Amount: ibctesting.TestCoin.Amount.String(), @@ -307,7 +307,7 @@ func (s *CallbacksTestSuite) TestOnAcknowledgementPacket() { userGasLimit = 600000 packetData = transfertypes.NewFungibleTokenPacketDataV2( - []*transfertypes.Token{ + []transfertypes.Token{ { Denom: ibctesting.TestCoin.GetDenom(), Amount: ibctesting.TestCoin.Amount.String(), @@ -640,7 +640,7 @@ func (s *CallbacksTestSuite) TestOnRecvPacket() { // set user gas limit above panic level in mock contract keeper userGasLimit = 600_000 packetData = transfertypes.NewFungibleTokenPacketDataV2( - []*transfertypes.Token{ + []transfertypes.Token{ { Denom: ibctesting.TestCoin.GetDenom(), Amount: ibctesting.TestCoin.Amount.String(), @@ -772,7 +772,7 @@ func (s *CallbacksTestSuite) TestWriteAcknowledgement() { // set user gas limit above panic level in mock contract keeper packetData = transfertypes.NewFungibleTokenPacketDataV2( - []*transfertypes.Token{ + []transfertypes.Token{ { Denom: ibctesting.TestCoin.GetDenom(), Amount: ibctesting.TestCoin.Amount.String(), @@ -991,7 +991,7 @@ func (s *CallbacksTestSuite) TestUnmarshalPacketData() { } expPacketDataICS20V2 := transfertypes.FungibleTokenPacketDataV2{ - Tokens: []*transfertypes.Token{ + Tokens: []transfertypes.Token{ { Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), diff --git a/modules/apps/transfer/ibc_module.go b/modules/apps/transfer/ibc_module.go index 62a5b975506..21d3dd45160 100644 --- a/modules/apps/transfer/ibc_module.go +++ b/modules/apps/transfer/ibc_module.go @@ -224,17 +224,13 @@ func (im IBCModule) OnRecvPacket( } } - events := make([]sdk.Event, 0, len(data.Tokens)+1) - for _, token := range data.Tokens { - events = append(events, sdk.NewEvent( - types.EventTypePacket, - sdk.NewAttribute(types.AttributeKeySender, data.Sender), - sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), - sdk.NewAttribute(types.AttributeKeyDenom, token.GetFullDenomPath()), - sdk.NewAttribute(types.AttributeKeyAmount, token.Amount), - sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), - )) - } + ctx.EventManager().EmitEvent(sdk.NewEvent( + types.EventTypePacket, + sdk.NewAttribute(types.AttributeKeySender, data.Sender), + sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), + sdk.NewAttribute(types.AttributeKeyTokens, types.Tokens(data.Tokens).String()), + sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), + )) eventAttributes := []sdk.Attribute{ sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), @@ -244,13 +240,11 @@ func (im IBCModule) OnRecvPacket( eventAttributes = append(eventAttributes, sdk.NewAttribute(types.AttributeKeyAckError, ackErr.Error())) } - events = append(events, sdk.NewEvent( + ctx.EventManager().EmitEvent(sdk.NewEvent( sdk.EventTypeMessage, eventAttributes..., )) - ctx.EventManager().EmitEvents(events) - // NOTE: acknowledgement will be written synchronously during IBC handler execution. return ack } @@ -276,25 +270,20 @@ func (im IBCModule) OnAcknowledgementPacket( return err } - events := make([]sdk.Event, 0, len(data.Tokens)+1) - for _, token := range data.Tokens { - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypePacket, - sdk.NewAttribute(sdk.AttributeKeySender, data.Sender), - sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), - sdk.NewAttribute(types.AttributeKeyDenom, token.GetFullDenomPath()), - sdk.NewAttribute(types.AttributeKeyAmount, token.Amount), - sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), - ), - ) - } - events = append(events, sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), - sdk.NewAttribute(types.AttributeKeyAck, ack.String()), - )) - ctx.EventManager().EmitEvents(events) + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypePacket, + sdk.NewAttribute(sdk.AttributeKeySender, data.Sender), + sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), + sdk.NewAttribute(types.AttributeKeyTokens, types.Tokens(data.Tokens).String()), + sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + sdk.NewAttribute(types.AttributeKeyAck, ack.String()), + ), + }) switch resp := ack.Response.(type) { case *channeltypes.Acknowledgement_Result: @@ -332,24 +321,19 @@ func (im IBCModule) OnTimeoutPacket( return err } - events := make([]sdk.Event, 0, len(data.Tokens)+1) - for _, token := range data.Tokens { - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeTimeout, - sdk.NewAttribute(sdk.AttributeKeySender, data.Sender), - sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), - sdk.NewAttribute(types.AttributeKeyDenom, token.GetFullDenomPath()), - sdk.NewAttribute(types.AttributeKeyAmount, token.Amount), - sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), - ), - ) - } - events = append(events, sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), - )) - ctx.EventManager().EmitEvents(events) + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeTimeout, + sdk.NewAttribute(sdk.AttributeKeySender, data.Sender), + sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), + sdk.NewAttribute(types.AttributeKeyTokens, types.Tokens(data.Tokens).String()), + sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + ), + }) return nil } diff --git a/modules/apps/transfer/ibc_module_test.go b/modules/apps/transfer/ibc_module_test.go index a67b31bf255..ddb2dd5b238 100644 --- a/modules/apps/transfer/ibc_module_test.go +++ b/modules/apps/transfer/ibc_module_test.go @@ -554,7 +554,7 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { "success: valid packet data multidenom with memo", func() { initialPacketData = types.FungibleTokenPacketDataV2{ - Tokens: []*types.Token{ + Tokens: []types.Token{ { Denom: "atom", Amount: ibctesting.TestCoin.Amount.String(), @@ -574,7 +574,7 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { "success: valid packet data multidenom without memo", func() { initialPacketData = types.FungibleTokenPacketDataV2{ - Tokens: []*types.Token{ + Tokens: []types.Token{ { Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), diff --git a/modules/apps/transfer/internal/convert/convert.go b/modules/apps/transfer/internal/convert/convert.go index 3c39bdeb20b..686ae6119b8 100644 --- a/modules/apps/transfer/internal/convert/convert.go +++ b/modules/apps/transfer/internal/convert/convert.go @@ -14,7 +14,7 @@ func PacketDataV1ToV2(packetData types.FungibleTokenPacketData) types.FungibleTo v2Denom, trace := ExtractDenomAndTraceFromV1Denom(packetData.Denom) return types.FungibleTokenPacketDataV2{ - Tokens: []*types.Token{ + Tokens: []types.Token{ { Denom: v2Denom, Amount: packetData.Amount, diff --git a/modules/apps/transfer/internal/convert/convert_test.go b/modules/apps/transfer/internal/convert/convert_test.go index 5860f328e8c..10aecb3c33d 100644 --- a/modules/apps/transfer/internal/convert/convert_test.go +++ b/modules/apps/transfer/internal/convert/convert_test.go @@ -26,7 +26,7 @@ func TestConvertPacketV1ToPacketV2(t *testing.T) { "success", types.NewFungibleTokenPacketData("transfer/channel-0/atom", "1000", sender, receiver, ""), types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: "atom", Amount: "1000", @@ -39,7 +39,7 @@ func TestConvertPacketV1ToPacketV2(t *testing.T) { "success with empty trace", types.NewFungibleTokenPacketData("atom", "1000", sender, receiver, ""), types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: "atom", Amount: "1000", @@ -52,7 +52,7 @@ func TestConvertPacketV1ToPacketV2(t *testing.T) { "success: base denom with '/'", types.NewFungibleTokenPacketData("transfer/channel-0/atom/withslash", "1000", sender, receiver, ""), types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: "atom/withslash", Amount: "1000", @@ -65,7 +65,7 @@ func TestConvertPacketV1ToPacketV2(t *testing.T) { "success: base denom with '/' at the end", types.NewFungibleTokenPacketData("transfer/channel-0/atom/", "1000", sender, receiver, ""), types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: "atom/", Amount: "1000", @@ -78,7 +78,7 @@ func TestConvertPacketV1ToPacketV2(t *testing.T) { "success: longer trace base denom with '/'", types.NewFungibleTokenPacketData("transfer/channel-0/transfer/channel-1/atom/pool", "1000", sender, receiver, ""), types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: "atom/pool", Amount: "1000", @@ -91,7 +91,7 @@ func TestConvertPacketV1ToPacketV2(t *testing.T) { "success: longer trace with non transfer port", types.NewFungibleTokenPacketData("transfer/channel-0/transfer/channel-1/transfer-custom/channel-2/atom", "1000", sender, receiver, ""), types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: "atom", Amount: "1000", @@ -104,7 +104,7 @@ func TestConvertPacketV1ToPacketV2(t *testing.T) { "success: base denom with slash, trace with non transfer port", types.NewFungibleTokenPacketData("transfer/channel-0/transfer/channel-1/transfer-custom/channel-2/atom/pool", "1000", sender, receiver, ""), types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: "atom/pool", Amount: "1000", diff --git a/modules/apps/transfer/keeper/mbt_relay_test.go b/modules/apps/transfer/keeper/mbt_relay_test.go index 6c0752e6b5f..d306643079a 100644 --- a/modules/apps/transfer/keeper/mbt_relay_test.go +++ b/modules/apps/transfer/keeper/mbt_relay_test.go @@ -151,7 +151,7 @@ func FungibleTokenPacketFromTla(packet TlaFungibleTokenPacket) FungibleTokenPack DestChannel: packet.DestChannel, DestPort: packet.DestPort, Data: types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: packet.Data.Amount, diff --git a/modules/apps/transfer/keeper/msg_server.go b/modules/apps/transfer/keeper/msg_server.go index 74bb202f521..e23672facb3 100644 --- a/modules/apps/transfer/keeper/msg_server.go +++ b/modules/apps/transfer/keeper/msg_server.go @@ -57,23 +57,21 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types. return nil, err } - events := make([]sdk.Event, 0, len(tokens)+1) - for _, token := range tokens { - k.Logger(ctx).Info("IBC fungible token transfer", "token", token.Denom, "amount", token.Amount.String(), "sender", msg.Sender, "receiver", msg.Receiver) - events = append(events, sdk.NewEvent( + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( types.EventTypeTransfer, sdk.NewAttribute(types.AttributeKeySender, msg.Sender), sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver), - sdk.NewAttribute(types.AttributeKeyDenom, token.Denom), - sdk.NewAttribute(types.AttributeKeyAmount, token.Amount.String()), + sdk.NewAttribute(types.AttributeKeyTokens, tokens.String()), sdk.NewAttribute(types.AttributeKeyMemo, msg.Memo), - )) - } - events = append(events, sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), - )) - ctx.EventManager().EmitEvents(events) + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + ), + }) + + k.Logger(ctx).Info("IBC fungible token transfer", "tokens", tokens, "sender", msg.Sender, "receiver", msg.Receiver) return &types.MsgTransferResponse{Sequence: sequence}, nil } diff --git a/modules/apps/transfer/keeper/msg_server_test.go b/modules/apps/transfer/keeper/msg_server_test.go index ae4aaef3107..e6d04001c4f 100644 --- a/modules/apps/transfer/keeper/msg_server_test.go +++ b/modules/apps/transfer/keeper/msg_server_test.go @@ -186,15 +186,7 @@ func (suite *KeeperTestSuite) TestMsgTransfer() { sdk.NewAttribute(types.AttributeKeySender, msg.Sender), sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver), sdk.NewAttribute(types.AttributeKeyMemo, msg.Memo), - sdk.NewAttribute(types.AttributeKeyDenom, coin1.Denom), - sdk.NewAttribute(types.AttributeKeyAmount, coin1.Amount.String()), - ), - sdk.NewEvent(types.EventTypeTransfer, - sdk.NewAttribute(types.AttributeKeySender, msg.Sender), - sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver), - sdk.NewAttribute(types.AttributeKeyMemo, msg.Memo), - sdk.NewAttribute(types.AttributeKeyDenom, coin2.Denom), - sdk.NewAttribute(types.AttributeKeyAmount, coin2.Amount.String()), + sdk.NewAttribute(types.AttributeKeyTokens, sdk.NewCoins(coin1, coin2).String()), ), sdk.NewEvent( sdk.EventTypeMessage, @@ -207,8 +199,7 @@ func (suite *KeeperTestSuite) TestMsgTransfer() { sdk.NewAttribute(types.AttributeKeySender, msg.Sender), sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver), sdk.NewAttribute(types.AttributeKeyMemo, msg.Memo), - sdk.NewAttribute(types.AttributeKeyDenom, coin1.Denom), - sdk.NewAttribute(types.AttributeKeyAmount, coin1.Amount.String()), + sdk.NewAttribute(types.AttributeKeyTokens, coin1.String()), ), sdk.NewEvent( sdk.EventTypeMessage, diff --git a/modules/apps/transfer/keeper/relay.go b/modules/apps/transfer/keeper/relay.go index bc5c39e5b3a..2c6dc104fbc 100644 --- a/modules/apps/transfer/keeper/relay.go +++ b/modules/apps/transfer/keeper/relay.go @@ -84,7 +84,7 @@ func (k Keeper) sendTransfer( return 0, errorsmod.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability") } - var tokens []*types.Token + var tokens []types.Token for _, coin := range coins { // NOTE: denomination and hex hash correctness checked during msg.ValidateBasic @@ -135,7 +135,7 @@ func (k Keeper) sendTransfer( } denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(fullDenomPath) - token := &types.Token{ + token := types.Token{ Denom: denom, Amount: coin.Amount.String(), Trace: trace, diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index 5ed95db4bab..168a165cb6a 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -412,7 +412,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) data := types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount.String(), @@ -495,7 +495,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacketSetsTotalEscrowAmountForSourceIBCT denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) data := types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount.String(), @@ -622,7 +622,7 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) data := types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount.String(), @@ -715,7 +715,7 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacketSetsTotalEscrowAmountFo denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) data := types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount.String(), @@ -838,7 +838,7 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() { denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) data := types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount.String(), @@ -924,7 +924,7 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacketSetsTotalEscrowAmountForSourceI denom, trace := convertinternal.ExtractDenomAndTraceFromV1Denom(denomTrace.GetFullDenomPath()) data := types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount.String(), diff --git a/modules/apps/transfer/types/events.go b/modules/apps/transfer/types/events.go index 20165a5e576..7a4bfaf2a04 100644 --- a/modules/apps/transfer/types/events.go +++ b/modules/apps/transfer/types/events.go @@ -11,7 +11,7 @@ const ( AttributeKeySender = "sender" AttributeKeyReceiver = "receiver" AttributeKeyDenom = "denom" - AttributeKeyAmount = "amount" + AttributeKeyTokens = "tokens" AttributeKeyRefundReceiver = "refund_receiver" AttributeKeyRefundDenom = "refund_denom" AttributeKeyRefundAmount = "refund_amount" diff --git a/modules/apps/transfer/types/msgs.go b/modules/apps/transfer/types/msgs.go index 2994e841951..29bfff3e9bd 100644 --- a/modules/apps/transfer/types/msgs.go +++ b/modules/apps/transfer/types/msgs.go @@ -108,7 +108,7 @@ func (msg MsgTransfer) ValidateBasic() error { } // GetTokens returns the tokens which will be transferred. -func (msg MsgTransfer) GetTokens() []sdk.Coin { +func (msg MsgTransfer) GetTokens() sdk.Coins { tokens := msg.Tokens if isValidToken(msg.Token) { tokens = []sdk.Coin{msg.Token} diff --git a/modules/apps/transfer/types/packet.go b/modules/apps/transfer/types/packet.go index 6ddf67cdbb5..81e46c72a53 100644 --- a/modules/apps/transfer/types/packet.go +++ b/modules/apps/transfer/types/packet.go @@ -99,7 +99,7 @@ func (ftpd FungibleTokenPacketData) GetCustomPacketData(key string) interface{} // NewFungibleTokenPacketDataV2 constructs a new NewFungibleTokenPacketDataV2 instance func NewFungibleTokenPacketDataV2( - tokens []*Token, + tokens []Token, sender, receiver string, memo string, ) FungibleTokenPacketDataV2 { diff --git a/modules/apps/transfer/types/packet.pb.go b/modules/apps/transfer/types/packet.pb.go index 020e16b93ef..94ef3c4a5a5 100644 --- a/modules/apps/transfer/types/packet.pb.go +++ b/modules/apps/transfer/types/packet.pb.go @@ -5,6 +5,7 @@ package types import ( fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" io "io" math "math" @@ -111,7 +112,7 @@ func (m *FungibleTokenPacketData) GetMemo() string { // https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#data-structures type FungibleTokenPacketDataV2 struct { // the tokens to be transferred - Tokens []*Token `protobuf:"bytes,1,rep,name=tokens,proto3" json:"tokens,omitempty"` + Tokens []Token `protobuf:"bytes,1,rep,name=tokens,proto3" json:"tokens"` // the sender address Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty"` // the recipient address on the destination chain @@ -153,7 +154,7 @@ func (m *FungibleTokenPacketDataV2) XXX_DiscardUnknown() { var xxx_messageInfo_FungibleTokenPacketDataV2 proto.InternalMessageInfo -func (m *FungibleTokenPacketDataV2) GetTokens() []*Token { +func (m *FungibleTokenPacketDataV2) GetTokens() []Token { if m != nil { return m.Tokens } @@ -256,28 +257,29 @@ func init() { } var fileDescriptor_653ca2ce9a5ca313 = []byte{ - // 325 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0x31, 0x4b, 0x03, 0x31, - 0x14, 0xc7, 0x9b, 0x5e, 0xaf, 0x68, 0xdc, 0x8e, 0xa2, 0x51, 0xe4, 0x28, 0x75, 0xa9, 0x83, 0x09, - 0x9c, 0x83, 0x82, 0x9b, 0x88, 0x8b, 0x8b, 0x16, 0x71, 0x70, 0xcb, 0xa5, 0xcf, 0x1a, 0xda, 0x24, - 0x47, 0x92, 0x3b, 0xf0, 0x53, 0xe8, 0x47, 0xf0, 0xe3, 0x38, 0x76, 0x74, 0x94, 0xf6, 0x8b, 0xc8, - 0xa5, 0x55, 0x6e, 0xa9, 0xe0, 0x96, 0xff, 0x3f, 0xff, 0xf7, 0xf8, 0x25, 0xef, 0xe1, 0x63, 0x99, - 0x0b, 0xc6, 0x8b, 0x62, 0x26, 0x05, 0xf7, 0xd2, 0x68, 0xc7, 0xbc, 0xe5, 0xda, 0x3d, 0x81, 0x65, - 0x55, 0xc6, 0x0a, 0x2e, 0xa6, 0xe0, 0x69, 0x61, 0x8d, 0x37, 0xc9, 0xa1, 0xcc, 0x05, 0x6d, 0x46, - 0xe9, 0x4f, 0x94, 0x56, 0xd9, 0xe0, 0x15, 0xe1, 0xbd, 0xeb, 0x52, 0x4f, 0x64, 0x3e, 0x83, 0x7b, - 0x33, 0x05, 0x7d, 0x1b, 0x6a, 0xaf, 0xb8, 0xe7, 0x49, 0x0f, 0xc7, 0x63, 0xd0, 0x46, 0x11, 0xd4, - 0x47, 0xc3, 0xed, 0xd1, 0x4a, 0x24, 0xbb, 0xb8, 0xcb, 0x95, 0x29, 0xb5, 0x27, 0xed, 0x60, 0xaf, - 0x55, 0xed, 0x3b, 0xd0, 0x63, 0xb0, 0x24, 0x5a, 0xf9, 0x2b, 0x95, 0x1c, 0xe0, 0x2d, 0x0b, 0x02, - 0x64, 0x05, 0x96, 0x74, 0xc2, 0xcd, 0xaf, 0x4e, 0x12, 0xdc, 0x51, 0xa0, 0x0c, 0x89, 0x83, 0x1f, - 0xce, 0x83, 0x77, 0x84, 0xf7, 0x37, 0x10, 0x3d, 0x64, 0xc9, 0x05, 0xee, 0xfa, 0xda, 0x74, 0x04, - 0xf5, 0xa3, 0xe1, 0x4e, 0x76, 0x44, 0xff, 0x7a, 0x1e, 0x0d, 0x0d, 0x46, 0xeb, 0x92, 0x06, 0x62, - 0x7b, 0x23, 0x62, 0xb4, 0x01, 0xb1, 0xd3, 0x40, 0xbc, 0xc1, 0x71, 0x68, 0xfc, 0xcf, 0x1f, 0xea, - 0xe1, 0xd8, 0x5b, 0x2e, 0x80, 0x44, 0xfd, 0xa8, 0x4e, 0x07, 0x71, 0x79, 0xf7, 0xb1, 0x48, 0xd1, - 0x7c, 0x91, 0xa2, 0xaf, 0x45, 0x8a, 0xde, 0x96, 0x69, 0x6b, 0xbe, 0x4c, 0x5b, 0x9f, 0xcb, 0xb4, - 0xf5, 0x78, 0x36, 0x91, 0xfe, 0xb9, 0xcc, 0xa9, 0x30, 0x8a, 0x09, 0xe3, 0x94, 0x71, 0x4c, 0xe6, - 0xe2, 0x64, 0x62, 0x58, 0x75, 0xce, 0x94, 0x19, 0x97, 0x33, 0x70, 0xf5, 0x12, 0x34, 0x86, 0xef, - 0x5f, 0x0a, 0x70, 0x79, 0x37, 0x4c, 0xfe, 0xf4, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x65, 0x55, 0xb8, - 0x16, 0x26, 0x02, 0x00, 0x00, + // 344 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0xb1, 0x4e, 0xfb, 0x30, + 0x10, 0xc6, 0xe3, 0x26, 0xa9, 0xfe, 0x7f, 0xb3, 0x45, 0x15, 0x84, 0x0a, 0x85, 0xaa, 0x2c, 0x65, + 0x20, 0x96, 0xc2, 0x00, 0x2b, 0x15, 0x62, 0x61, 0x81, 0x0a, 0x31, 0xb0, 0x39, 0xee, 0x11, 0xac, + 0x36, 0xb9, 0x28, 0x76, 0x22, 0xf1, 0x14, 0xf0, 0x14, 0x3c, 0x4b, 0xc7, 0x8e, 0x4c, 0x08, 0xb5, + 0x2f, 0x82, 0xe2, 0x14, 0x94, 0xa5, 0x48, 0x6c, 0xf7, 0x7d, 0xfe, 0xee, 0xf4, 0xb3, 0x7d, 0xf4, + 0x58, 0xc6, 0x82, 0xf1, 0x3c, 0x9f, 0x4b, 0xc1, 0xb5, 0xc4, 0x4c, 0x31, 0x5d, 0xf0, 0x4c, 0x3d, + 0x42, 0xc1, 0xaa, 0x88, 0xe5, 0x5c, 0xcc, 0x40, 0x87, 0x79, 0x81, 0x1a, 0xbd, 0x03, 0x19, 0x8b, + 0xb0, 0x1d, 0x0d, 0xbf, 0xa3, 0x61, 0x15, 0xf5, 0x7b, 0x09, 0x26, 0x68, 0x82, 0xac, 0xae, 0x9a, + 0x9e, 0xe1, 0x0b, 0xa1, 0x7b, 0x57, 0x65, 0x96, 0xc8, 0x78, 0x0e, 0x77, 0x38, 0x83, 0xec, 0xc6, + 0x4c, 0xbc, 0xe4, 0x9a, 0x7b, 0x3d, 0xea, 0x4e, 0x21, 0xc3, 0xd4, 0x27, 0x03, 0x32, 0xfa, 0x3f, + 0x69, 0x84, 0xb7, 0x4b, 0xbb, 0x3c, 0xc5, 0x32, 0xd3, 0x7e, 0xc7, 0xd8, 0x1b, 0x55, 0xfb, 0x0a, + 0xb2, 0x29, 0x14, 0xbe, 0xdd, 0xf8, 0x8d, 0xf2, 0xfa, 0xf4, 0x5f, 0x01, 0x02, 0x64, 0x05, 0x85, + 0xef, 0x98, 0x93, 0x1f, 0xed, 0x79, 0xd4, 0x49, 0x21, 0x45, 0xdf, 0x35, 0xbe, 0xa9, 0x87, 0x6f, + 0x84, 0xee, 0x6f, 0x21, 0xba, 0x8f, 0xbc, 0x0b, 0xda, 0xd5, 0xb5, 0xa9, 0x7c, 0x32, 0xb0, 0x47, + 0x3b, 0xd1, 0x51, 0xf8, 0xdb, 0xa5, 0x43, 0x33, 0x60, 0xec, 0x2c, 0x3e, 0x0e, 0xad, 0xc9, 0xa6, + 0xb1, 0x05, 0xda, 0xd9, 0x0a, 0x6a, 0x6f, 0x01, 0x75, 0x5a, 0xa0, 0xd7, 0xd4, 0x35, 0xe3, 0xff, + 0xf8, 0x4e, 0x3d, 0xea, 0xea, 0x82, 0x0b, 0xf0, 0xed, 0x81, 0x5d, 0xa7, 0x8d, 0x18, 0xdf, 0x2e, + 0x56, 0x01, 0x59, 0xae, 0x02, 0xf2, 0xb9, 0x0a, 0xc8, 0xeb, 0x3a, 0xb0, 0x96, 0xeb, 0xc0, 0x7a, + 0x5f, 0x07, 0xd6, 0xc3, 0x59, 0x22, 0xf5, 0x53, 0x19, 0x87, 0x02, 0x53, 0x26, 0x50, 0xa5, 0xa8, + 0x98, 0x8c, 0xc5, 0x49, 0x82, 0xac, 0x3a, 0x67, 0x29, 0x4e, 0xcb, 0x39, 0xa8, 0x7a, 0x41, 0x5a, + 0x8b, 0xa1, 0x9f, 0x73, 0x50, 0x71, 0xd7, 0xfc, 0xf0, 0xe9, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xe1, 0x0d, 0x6c, 0x28, 0x42, 0x02, 0x00, 0x00, } func (m *FungibleTokenPacketData) Marshal() (dAtA []byte, err error) { @@ -806,7 +808,7 @@ func (m *FungibleTokenPacketDataV2) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Tokens = append(m.Tokens, &Token{}) + m.Tokens = append(m.Tokens, Token{}) if err := m.Tokens[len(m.Tokens)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } diff --git a/modules/apps/transfer/types/packet_test.go b/modules/apps/transfer/types/packet_test.go index 759253ccfec..9292f207f7f 100644 --- a/modules/apps/transfer/types/packet_test.go +++ b/modules/apps/transfer/types/packet_test.go @@ -171,7 +171,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { { "success: valid packet", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount, @@ -187,7 +187,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { { "success: valid packet with memo", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount, @@ -203,7 +203,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { { "success: valid packet with large amount", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: largeAmount, @@ -219,7 +219,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { { "failure: invalid denom", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: "", Amount: amount, @@ -235,7 +235,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { { "failure: invalid empty amount", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: "", @@ -251,7 +251,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { { "failure: invalid empty token array", types.NewFungibleTokenPacketDataV2( - []*types.Token{}, + []types.Token{}, sender, receiver, "", @@ -261,7 +261,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { { "failure: invalid zero amount", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: "0", @@ -277,7 +277,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { { "failure: invalid negative amount", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: "-100", @@ -293,7 +293,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { { "failure: invalid large amount", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: invalidLargeAmount, @@ -309,7 +309,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { { "failure: missing sender address", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount, @@ -325,7 +325,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { { "failure: missing recipient address", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount, @@ -363,7 +363,7 @@ func TestGetPacketSender(t *testing.T) { { "non-empty sender field", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount, @@ -379,7 +379,7 @@ func TestGetPacketSender(t *testing.T) { { "empty sender field", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount, @@ -410,7 +410,7 @@ func TestPacketDataProvider(t *testing.T) { { "success: src_callback key in memo", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount, @@ -428,7 +428,7 @@ func TestPacketDataProvider(t *testing.T) { { "success: src_callback key in memo with additional fields", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount, @@ -446,7 +446,7 @@ func TestPacketDataProvider(t *testing.T) { { "success: src_callback has string value", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount, @@ -461,7 +461,7 @@ func TestPacketDataProvider(t *testing.T) { { "failure: src_callback key not found memo", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount, @@ -476,7 +476,7 @@ func TestPacketDataProvider(t *testing.T) { { "failure: empty memo", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount, @@ -491,7 +491,7 @@ func TestPacketDataProvider(t *testing.T) { { "failure: non-json memo", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount, @@ -522,7 +522,7 @@ func TestFungibleTokenPacketDataOmitEmpty(t *testing.T) { { "empty memo field, resulting marshalled bytes should not contain the memo field", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount, @@ -538,7 +538,7 @@ func TestFungibleTokenPacketDataOmitEmpty(t *testing.T) { { "non-empty memo field, resulting marshalled bytes should contain the memo field", types.NewFungibleTokenPacketDataV2( - []*types.Token{ + []types.Token{ { Denom: denom, Amount: amount, diff --git a/modules/apps/transfer/types/token.go b/modules/apps/transfer/types/token.go index bdfb7880577..a91dbb97792 100644 --- a/modules/apps/transfer/types/token.go +++ b/modules/apps/transfer/types/token.go @@ -37,3 +37,25 @@ func (t Token) GetFullDenomPath() string { return strings.Join(append(t.Trace, t.Denom), "/") } + +// Tokens is a set of Token +type Tokens []Token + +// String prints out the tokens array as a string. +// If the array is empty, an empty string is returned +func (tokens Tokens) String() string { + if len(tokens) == 0 { + return "" + } else if len(tokens) == 1 { + return tokens[0].String() + } + + var out strings.Builder + for _, token := range tokens[:len(tokens)-1] { + out.WriteString(token.String()) // nolint:revive // no error returned by WriteString + out.WriteByte(',') //nolint:revive // no error returned by WriteByte + + } + out.WriteString(tokens[len(tokens)-1].String()) //nolint:revive + return out.String() +} diff --git a/modules/apps/transfer/types/token_test.go b/modules/apps/transfer/types/token_test.go index deb468e7066..afc008b6ed1 100644 --- a/modules/apps/transfer/types/token_test.go +++ b/modules/apps/transfer/types/token_test.go @@ -29,7 +29,7 @@ func TestGetFullDenomPath(t *testing.T) { { "denom path with trace", NewFungibleTokenPacketDataV2( - []*Token{ + []Token{ { Denom: denom, Amount: amount, @@ -45,7 +45,7 @@ func TestGetFullDenomPath(t *testing.T) { { "nil trace", NewFungibleTokenPacketDataV2( - []*Token{ + []Token{ { Denom: denom, Amount: amount, @@ -61,7 +61,7 @@ func TestGetFullDenomPath(t *testing.T) { { "empty string trace", NewFungibleTokenPacketDataV2( - []*Token{ + []Token{ { Denom: denom, Amount: amount, diff --git a/proto/ibc/applications/transfer/v2/packet.proto b/proto/ibc/applications/transfer/v2/packet.proto index 4dfaf107062..3614bcb85ed 100644 --- a/proto/ibc/applications/transfer/v2/packet.proto +++ b/proto/ibc/applications/transfer/v2/packet.proto @@ -4,6 +4,8 @@ package ibc.applications.transfer.v2; option go_package = "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types"; +import "gogoproto/gogo.proto"; + // FungibleTokenPacketData defines a struct for the packet payload // See FungibleTokenPacketData spec: // https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#data-structures @@ -25,7 +27,7 @@ message FungibleTokenPacketData { // https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#data-structures message FungibleTokenPacketDataV2 { // the tokens to be transferred - repeated Token tokens = 1; + repeated Token tokens = 1 [(gogoproto.nullable) = false]; // the sender address string sender = 2; // the recipient address on the destination chain From 43877df24f1f4f9c79d85ef1481e01d4cfc67a92 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Wed, 22 May 2024 11:23:50 +0200 Subject: [PATCH 22/42] imp: check length tokens array against maximum allowed (#6349) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * check length of tokens array against maximum allowed * chore: add note on arbitrary selection --------- Co-authored-by: Colin Axnér <25233464+colin-axner@users.noreply.github.com> --- modules/apps/transfer/types/msgs.go | 5 +++++ modules/apps/transfer/types/msgs_test.go | 1 + 2 files changed, 6 insertions(+) diff --git a/modules/apps/transfer/types/msgs.go b/modules/apps/transfer/types/msgs.go index 29bfff3e9bd..85e10c4db31 100644 --- a/modules/apps/transfer/types/msgs.go +++ b/modules/apps/transfer/types/msgs.go @@ -15,6 +15,7 @@ import ( const ( MaximumReceiverLength = 2048 // maximum length of the receiver address in bytes (value chosen arbitrarily) MaximumMemoLength = 32768 // maximum length of the memo in bytes (value chosen arbitrarily) + MaximumTokensLength = 100 // maximum number of tokens that can be transferred in a single message (value chosen arbitrarily) ) var ( @@ -81,6 +82,10 @@ func (msg MsgTransfer) ValidateBasic() error { return errorsmod.Wrap(ErrInvalidAmount, "cannot fill both token and token array") } + if len(msg.Tokens) > MaximumTokensLength { + return errorsmod.Wrapf(ibcerrors.ErrInvalidCoins, "number of tokens must not exceed %d", MaximumTokensLength) + } + _, err := sdk.AccAddressFromBech32(msg.Sender) if err != nil { return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) diff --git a/modules/apps/transfer/types/msgs_test.go b/modules/apps/transfer/types/msgs_test.go index e98400bfa45..602551980c6 100644 --- a/modules/apps/transfer/types/msgs_test.go +++ b/modules/apps/transfer/types/msgs_test.go @@ -74,6 +74,7 @@ func TestMsgTransferValidation(t *testing.T) { {"multidenom: invalid denom", types.NewMsgTransfer(validPort, validChannel, coins.Add(invalidDenomCoins...), sender, receiver, timeoutHeight, 0, ""), false}, {"multidenom: invalid ibc denom", types.NewMsgTransfer(validPort, validChannel, coins.Add(invalidIBCCoins...), sender, receiver, timeoutHeight, 0, ""), false}, {"multidenom: zero coins", types.NewMsgTransfer(validPort, validChannel, zeroCoins, sender, receiver, timeoutHeight, 0, ""), false}, + {"multidenom: too many coins", types.NewMsgTransfer(validPort, validChannel, make([]sdk.Coin, types.MaximumTokensLength+1), sender, receiver, timeoutHeight, 0, ""), false}, } for i, tc := range testCases { From 8eae033732183be839b3d368a6b97720768e7b3b Mon Sep 17 00:00:00 2001 From: DimitrisJim Date: Wed, 22 May 2024 13:29:10 +0300 Subject: [PATCH 23/42] Modify UnmarshalPacketData interface to allow additional args (#6341) * api(port)!: Allow passing of context, port and channel identifier to unmarshal packet data interface as disussed. This allows us to grab the app version in transfer and unmarshal the packet based on that instead of a hacky unmarshal v2 then v1 and whatever happens. * lint: as we do * callbacks: fix signature of UnmarshalPacketData as per changes, make refactors to hopefully simplify signatures. * chore: lint and remove some todos. * review: address feedback. --- .../controller/ibc_middleware.go | 2 +- .../controller/ibc_middleware_test.go | 6 ++-- .../27-interchain-accounts/host/ibc_module.go | 2 +- .../host/ibc_module_test.go | 6 ++-- modules/apps/29-fee/ibc_middleware.go | 4 +-- modules/apps/29-fee/ibc_middleware_test.go | 6 ++-- modules/apps/callbacks/callbacks_test.go | 15 ++++++-- modules/apps/callbacks/ibc_middleware.go | 22 ++++++++---- modules/apps/callbacks/ibc_middleware_test.go | 17 +++++---- modules/apps/callbacks/types/callbacks.go | 35 ++++++++++++------- .../apps/callbacks/types/callbacks_test.go | 33 +++++++++++++++-- modules/apps/callbacks/types/export_test.go | 13 ------- modules/apps/transfer/ibc_module.go | 2 +- modules/apps/transfer/ibc_module_test.go | 2 +- modules/core/05-port/types/module.go | 2 +- testing/mock/ibc_module.go | 2 +- 16 files changed, 110 insertions(+), 59 deletions(-) diff --git a/modules/apps/27-interchain-accounts/controller/ibc_middleware.go b/modules/apps/27-interchain-accounts/controller/ibc_middleware.go index fe9ef65a026..dc27eeda2d8 100644 --- a/modules/apps/27-interchain-accounts/controller/ibc_middleware.go +++ b/modules/apps/27-interchain-accounts/controller/ibc_middleware.go @@ -340,7 +340,7 @@ func (im IBCMiddleware) GetAppVersion(ctx sdk.Context, portID, channelID string) // UnmarshalPacketData attempts to unmarshal the provided packet data bytes // into an InterchainAccountPacketData. This function implements the optional // PacketDataUnmarshaler interface required for ADR 008 support. -func (IBCMiddleware) UnmarshalPacketData(bz []byte) (interface{}, error) { +func (IBCMiddleware) UnmarshalPacketData(_ sdk.Context, _, _ string, bz []byte) (interface{}, error) { var data icatypes.InterchainAccountPacketData err := data.UnmarshalJSON(bz) if err != nil { diff --git a/modules/apps/27-interchain-accounts/controller/ibc_middleware_test.go b/modules/apps/27-interchain-accounts/controller/ibc_middleware_test.go index 95a25ac65a9..e9ab95cc1b9 100644 --- a/modules/apps/27-interchain-accounts/controller/ibc_middleware_test.go +++ b/modules/apps/27-interchain-accounts/controller/ibc_middleware_test.go @@ -1276,13 +1276,15 @@ func (suite *InterchainAccountsTestSuite) TestPacketDataUnmarshalerInterface() { Memo: "", } - packetData, err := controller.IBCMiddleware{}.UnmarshalPacketData(expPacketData.GetBytes()) + // Context, port identifier and channel identifier are unused for controller. + packetData, err := controller.IBCMiddleware{}.UnmarshalPacketData(suite.chainA.GetContext(), "", "", expPacketData.GetBytes()) suite.Require().NoError(err) suite.Require().Equal(expPacketData, packetData) // test invalid packet data invalidPacketData := []byte("invalid packet data") - packetData, err = controller.IBCMiddleware{}.UnmarshalPacketData(invalidPacketData) + // Context, port identifier and channel identifier are not used for controller. + packetData, err = controller.IBCMiddleware{}.UnmarshalPacketData(suite.chainA.GetContext(), "", "", invalidPacketData) suite.Require().Error(err) suite.Require().Nil(packetData) } diff --git a/modules/apps/27-interchain-accounts/host/ibc_module.go b/modules/apps/27-interchain-accounts/host/ibc_module.go index 3829a4c9acd..099e51e92cd 100644 --- a/modules/apps/27-interchain-accounts/host/ibc_module.go +++ b/modules/apps/27-interchain-accounts/host/ibc_module.go @@ -183,7 +183,7 @@ func (IBCModule) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID string, pr // UnmarshalPacketData attempts to unmarshal the provided packet data bytes // into an InterchainAccountPacketData. This function implements the optional // PacketDataUnmarshaler interface required for ADR 008 support. -func (IBCModule) UnmarshalPacketData(bz []byte) (interface{}, error) { +func (IBCModule) UnmarshalPacketData(_ sdk.Context, _, _ string, bz []byte) (interface{}, error) { var data icatypes.InterchainAccountPacketData err := data.UnmarshalJSON(bz) if err != nil { diff --git a/modules/apps/27-interchain-accounts/host/ibc_module_test.go b/modules/apps/27-interchain-accounts/host/ibc_module_test.go index 98dbb2b36a0..9ffb67a8f4d 100644 --- a/modules/apps/27-interchain-accounts/host/ibc_module_test.go +++ b/modules/apps/27-interchain-accounts/host/ibc_module_test.go @@ -883,13 +883,15 @@ func (suite *InterchainAccountsTestSuite) TestPacketDataUnmarshalerInterface() { Memo: "", } - packetData, err := icahost.IBCModule{}.UnmarshalPacketData(expPacketData.GetBytes()) + // Context, port identifier and channel identifier are unused for host. + packetData, err := icahost.IBCModule{}.UnmarshalPacketData(suite.chainA.GetContext(), "", "", expPacketData.GetBytes()) suite.Require().NoError(err) suite.Require().Equal(expPacketData, packetData) // test invalid packet data invalidPacketData := []byte("invalid packet data") - packetData, err = icahost.IBCModule{}.UnmarshalPacketData(invalidPacketData) + // Context, port identifier and channel identifier are unused for host. + packetData, err = icahost.IBCModule{}.UnmarshalPacketData(suite.chainA.GetContext(), "", "", invalidPacketData) suite.Require().Error(err) suite.Require().Nil(packetData) } diff --git a/modules/apps/29-fee/ibc_middleware.go b/modules/apps/29-fee/ibc_middleware.go index 218efb08882..671206eab8f 100644 --- a/modules/apps/29-fee/ibc_middleware.go +++ b/modules/apps/29-fee/ibc_middleware.go @@ -472,11 +472,11 @@ func (im IBCMiddleware) GetAppVersion(ctx sdk.Context, portID, channelID string) // UnmarshalPacketData attempts to use the underlying app to unmarshal the packet data. // If the underlying app does not support the PacketDataUnmarshaler interface, an error is returned. // This function implements the optional PacketDataUnmarshaler interface required for ADR 008 support. -func (im IBCMiddleware) UnmarshalPacketData(bz []byte) (interface{}, error) { +func (im IBCMiddleware) UnmarshalPacketData(ctx sdk.Context, portID, channelID string, bz []byte) (interface{}, error) { unmarshaler, ok := im.app.(porttypes.PacketDataUnmarshaler) if !ok { return nil, errorsmod.Wrapf(types.ErrUnsupportedAction, "underlying app does not implement %T", (*porttypes.PacketDataUnmarshaler)(nil)) } - return unmarshaler.UnmarshalPacketData(bz) + return unmarshaler.UnmarshalPacketData(ctx, portID, channelID, bz) } diff --git a/modules/apps/29-fee/ibc_middleware_test.go b/modules/apps/29-fee/ibc_middleware_test.go index fc16fd900fc..dc59f277d3f 100644 --- a/modules/apps/29-fee/ibc_middleware_test.go +++ b/modules/apps/29-fee/ibc_middleware_test.go @@ -1573,7 +1573,8 @@ func (suite *FeeTestSuite) TestPacketDataUnmarshalerInterface() { feeModule, ok := cbs.(porttypes.PacketDataUnmarshaler) suite.Require().True(ok) - packetData, err := feeModule.UnmarshalPacketData(ibcmock.MockPacketData) + // Context, port identifier, channel identifier are not used in current wiring of fee. + packetData, err := feeModule.UnmarshalPacketData(suite.chainA.GetContext(), "", "", ibcmock.MockPacketData) suite.Require().NoError(err) suite.Require().Equal(ibcmock.MockPacketData, packetData) } @@ -1582,7 +1583,8 @@ func (suite *FeeTestSuite) TestPacketDataUnmarshalerInterfaceError() { // test the case when the underlying application cannot be casted to a PacketDataUnmarshaler mockFeeMiddleware := ibcfee.NewIBCMiddleware(nil, feekeeper.Keeper{}) - _, err := mockFeeMiddleware.UnmarshalPacketData(ibcmock.MockPacketData) + // Context, port identifier, channel identifier are not used in mockFeeMiddleware. + _, err := mockFeeMiddleware.UnmarshalPacketData(suite.chainA.GetContext(), "", "", ibcmock.MockPacketData) expError := errorsmod.Wrapf(types.ErrUnsupportedAction, "underlying app does not implement %T", (*porttypes.PacketDataUnmarshaler)(nil)) suite.Require().ErrorIs(err, expError) } diff --git a/modules/apps/callbacks/callbacks_test.go b/modules/apps/callbacks/callbacks_test.go index 5c8ba271a9f..ef88db93483 100644 --- a/modules/apps/callbacks/callbacks_test.go +++ b/modules/apps/callbacks/callbacks_test.go @@ -11,6 +11,7 @@ import ( "cosmossdk.io/log" sdkmath "cosmossdk.io/math" + storetypes "cosmossdk.io/store/types" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" @@ -24,6 +25,7 @@ import ( icatypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/types" feetypes "github.com/cosmos/ibc-go/v8/modules/apps/29-fee/types" transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" ibctesting "github.com/cosmos/ibc-go/v8/testing" @@ -291,17 +293,24 @@ func (s *CallbacksTestSuite) AssertHasExecutedExpectedCallbackWithFee( // GetExpectedEvent returns the expected event for a callback. func GetExpectedEvent( - packetDataUnmarshaler porttypes.PacketDataUnmarshaler, remainingGas uint64, data []byte, srcPortID, + ctx sdk.Context, packetDataUnmarshaler porttypes.PacketDataUnmarshaler, remainingGas uint64, data []byte, srcPortID, eventPortID, eventChannelID string, seq uint64, callbackType types.CallbackType, expError error, ) (abci.Event, bool) { var ( callbackData types.CallbackData err error ) + + // Set up gas meter with remainingGas. + gasMeter := storetypes.NewGasMeter(remainingGas) + ctx = ctx.WithGasMeter(gasMeter) + + // Mock packet. + packet := channeltypes.NewPacket(data, 0, srcPortID, "", "", "", clienttypes.ZeroHeight(), 0) if callbackType == types.CallbackTypeReceivePacket { - callbackData, err = types.GetDestCallbackData(packetDataUnmarshaler, data, srcPortID, remainingGas, maxCallbackGas) + callbackData, err = types.GetDestCallbackData(ctx, packetDataUnmarshaler, packet, maxCallbackGas) } else { - callbackData, err = types.GetSourceCallbackData(packetDataUnmarshaler, data, srcPortID, remainingGas, maxCallbackGas) + callbackData, err = types.GetSourceCallbackData(ctx, packetDataUnmarshaler, packet, maxCallbackGas) } if err != nil { return abci.Event{}, false diff --git a/modules/apps/callbacks/ibc_middleware.go b/modules/apps/callbacks/ibc_middleware.go index 0ab1f5f9887..816305913d8 100644 --- a/modules/apps/callbacks/ibc_middleware.go +++ b/modules/apps/callbacks/ibc_middleware.go @@ -99,7 +99,10 @@ func (im IBCMiddleware) SendPacket( return 0, err } - callbackData, err := types.GetSourceCallbackData(im.app, data, sourcePort, ctx.GasMeter().GasRemaining(), im.maxCallbackGas) + // packet is created withouth destination information present, GetSourceCallbackData does not use these. + packet := channeltypes.NewPacket(data, seq, sourcePort, sourceChannel, "", "", timeoutHeight, timeoutTimestamp) + + callbackData, err := types.GetSourceCallbackData(ctx, im.app, packet, im.maxCallbackGas) // SendPacket is not blocked if the packet does not opt-in to callbacks if err != nil { return seq, nil @@ -138,7 +141,7 @@ func (im IBCMiddleware) OnAcknowledgementPacket( } callbackData, err := types.GetSourceCallbackData( - im.app, packet.GetData(), packet.GetSourcePort(), ctx.GasMeter().GasRemaining(), im.maxCallbackGas, + ctx, im.app, packet, im.maxCallbackGas, ) // OnAcknowledgementPacket is not blocked if the packet does not opt-in to callbacks if err != nil { @@ -172,7 +175,7 @@ func (im IBCMiddleware) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Pac } callbackData, err := types.GetSourceCallbackData( - im.app, packet.GetData(), packet.GetSourcePort(), ctx.GasMeter().GasRemaining(), im.maxCallbackGas, + ctx, im.app, packet, im.maxCallbackGas, ) // OnTimeoutPacket is not blocked if the packet does not opt-in to callbacks if err != nil { @@ -208,7 +211,7 @@ func (im IBCMiddleware) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet } callbackData, err := types.GetDestCallbackData( - im.app, packet.GetData(), packet.GetSourcePort(), ctx.GasMeter().GasRemaining(), im.maxCallbackGas, + ctx, im.app, packet, im.maxCallbackGas, ) // OnRecvPacket is not blocked if the packet does not opt-in to callbacks if err != nil { @@ -245,8 +248,13 @@ func (im IBCMiddleware) WriteAcknowledgement( return err } + chanPacket, ok := packet.(channeltypes.Packet) + if !ok { + panic(fmt.Errorf("expected type %T, got %T", &channeltypes.Packet{}, packet)) + } + callbackData, err := types.GetDestCallbackData( - im.app, packet.GetData(), packet.GetSourcePort(), ctx.GasMeter().GasRemaining(), im.maxCallbackGas, + ctx, im.app, chanPacket, im.maxCallbackGas, ) // WriteAcknowledgement is not blocked if the packet does not opt-in to callbacks if err != nil { @@ -417,6 +425,6 @@ func (im IBCMiddleware) GetAppVersion(ctx sdk.Context, portID, channelID string) // UnmarshalPacketData defers to the underlying app to unmarshal the packet data. // This function implements the optional PacketDataUnmarshaler interface. -func (im IBCMiddleware) UnmarshalPacketData(bz []byte) (interface{}, error) { - return im.app.UnmarshalPacketData(bz) +func (im IBCMiddleware) UnmarshalPacketData(ctx sdk.Context, portID, channelID string, bz []byte) (interface{}, error) { + return im.app.UnmarshalPacketData(ctx, portID, channelID, bz) } diff --git a/modules/apps/callbacks/ibc_middleware_test.go b/modules/apps/callbacks/ibc_middleware_test.go index fa8ee5794ab..0b487d93611 100644 --- a/modules/apps/callbacks/ibc_middleware_test.go +++ b/modules/apps/callbacks/ibc_middleware_test.go @@ -200,7 +200,7 @@ func (s *CallbacksTestSuite) TestSendPacket() { s.Require().Equal(uint64(1), seq) expEvent, exists := GetExpectedEvent( - transferICS4Wrapper.(porttypes.PacketDataUnmarshaler), gasLimit, packetData.GetBytes(), s.path.EndpointA.ChannelConfig.PortID, + ctx, transferICS4Wrapper.(porttypes.PacketDataUnmarshaler), gasLimit, packetData.GetBytes(), s.path.EndpointA.ChannelConfig.PortID, s.path.EndpointA.ChannelConfig.PortID, s.path.EndpointA.ChannelID, seq, types.CallbackTypeSendPacket, nil, ) if exists { @@ -381,7 +381,7 @@ func (s *CallbacksTestSuite) TestOnAcknowledgementPacket() { s.Require().Equal(uint8(1), sourceStatefulCounter) expEvent, exists := GetExpectedEvent( - transferStack.(porttypes.PacketDataUnmarshaler), gasLimit, packet.Data, packet.SourcePort, + ctx, transferStack.(porttypes.PacketDataUnmarshaler), gasLimit, packet.Data, packet.SourcePort, packet.SourcePort, packet.SourceChannel, packet.Sequence, types.CallbackTypeAcknowledgementPacket, nil, ) s.Require().True(exists) @@ -543,7 +543,7 @@ func (s *CallbacksTestSuite) TestOnTimeoutPacket() { s.Require().Equal(uint8(2), sourceStatefulCounter) expEvent, exists := GetExpectedEvent( - transferStack.(porttypes.PacketDataUnmarshaler), gasLimit, packet.Data, packet.SourcePort, + ctx, transferStack.(porttypes.PacketDataUnmarshaler), gasLimit, packet.Data, packet.SourcePort, packet.SourcePort, packet.SourceChannel, packet.Sequence, types.CallbackTypeTimeoutPacket, nil, ) s.Require().True(exists) @@ -712,7 +712,7 @@ func (s *CallbacksTestSuite) TestOnRecvPacket() { s.Require().Equal(uint8(1), destStatefulCounter) expEvent, exists := GetExpectedEvent( - transferStack.(porttypes.PacketDataUnmarshaler), gasLimit, packet.Data, packet.SourcePort, + ctx, transferStack.(porttypes.PacketDataUnmarshaler), gasLimit, packet.Data, packet.SourcePort, packet.DestinationPort, packet.DestinationChannel, packet.Sequence, types.CallbackTypeReceivePacket, nil, ) s.Require().True(exists) @@ -814,7 +814,7 @@ func (s *CallbacksTestSuite) TestWriteAcknowledgement() { s.Require().NoError(err) expEvent, exists := GetExpectedEvent( - transferICS4Wrapper.(porttypes.PacketDataUnmarshaler), gasLimit, packet.Data, packet.SourcePort, + ctx, transferICS4Wrapper.(porttypes.PacketDataUnmarshaler), gasLimit, packet.Data, packet.SourcePort, packet.DestinationPort, packet.DestinationChannel, packet.Sequence, types.CallbackTypeReceivePacket, nil, ) if exists { @@ -1003,15 +1003,18 @@ func (s *CallbacksTestSuite) TestUnmarshalPacketData() { Memo: fmt.Sprintf(`{"src_callback": {"address": "%s"}, "dest_callback": {"address":"%s"}}`, ibctesting.TestAccAddress, ibctesting.TestAccAddress), } + portID := s.path.EndpointA.ChannelConfig.PortID + channelID := s.path.EndpointA.ChannelID + // Unmarshal ICS20 v1 packet data data := expPacketDataICS20V1.GetBytes() - packetData, err := unmarshalerStack.UnmarshalPacketData(data) + packetData, err := unmarshalerStack.UnmarshalPacketData(s.chainA.GetContext(), portID, channelID, data) s.Require().NoError(err) s.Require().Equal(expPacketDataICS20V2, packetData) // Unmarshal ICS20 v1 packet data data = expPacketDataICS20V2.GetBytes() - packetData, err = unmarshalerStack.UnmarshalPacketData(data) + packetData, err = unmarshalerStack.UnmarshalPacketData(s.chainA.GetContext(), portID, channelID, data) s.Require().NoError(err) s.Require().Equal(expPacketDataICS20V2, packetData) } diff --git a/modules/apps/callbacks/types/callbacks.go b/modules/apps/callbacks/types/callbacks.go index 03c4128fb31..b7eaabec93b 100644 --- a/modules/apps/callbacks/types/callbacks.go +++ b/modules/apps/callbacks/types/callbacks.go @@ -6,6 +6,9 @@ import ( errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" ) @@ -67,18 +70,31 @@ type CallbackData struct { // GetSourceCallbackData parses the packet data and returns the source callback data. func GetSourceCallbackData( + ctx sdk.Context, packetDataUnmarshaler porttypes.PacketDataUnmarshaler, - data []byte, srcPortID string, remainingGas uint64, maxGas uint64, + packet channeltypes.Packet, + maxGas uint64, ) (CallbackData, error) { - return getCallbackData(packetDataUnmarshaler, data, srcPortID, remainingGas, maxGas, SourceCallbackKey) + packetData, err := packetDataUnmarshaler.UnmarshalPacketData(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetData()) + if err != nil { + return CallbackData{}, errorsmod.Wrap(ErrCannotUnmarshalPacketData, err.Error()) + } + + return getCallbackData(packetData, packet.GetSourcePort(), ctx.GasMeter().GasRemaining(), maxGas, SourceCallbackKey) } // GetDestCallbackData parses the packet data and returns the destination callback data. func GetDestCallbackData( + ctx sdk.Context, packetDataUnmarshaler porttypes.PacketDataUnmarshaler, - data []byte, srcPortID string, remainingGas, maxGas uint64, + packet channeltypes.Packet, maxGas uint64, ) (CallbackData, error) { - return getCallbackData(packetDataUnmarshaler, data, srcPortID, remainingGas, maxGas, DestinationCallbackKey) + packetData, err := packetDataUnmarshaler.UnmarshalPacketData(ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetData()) + if err != nil { + return CallbackData{}, errorsmod.Wrap(ErrCannotUnmarshalPacketData, err.Error()) + } + + return getCallbackData(packetData, packet.GetSourcePort(), ctx.GasMeter().GasRemaining(), maxGas, DestinationCallbackKey) } // getCallbackData parses the packet data and returns the callback data. @@ -86,16 +102,11 @@ func GetDestCallbackData( // The addressGetter and gasLimitGetter functions are used to retrieve the callback // address and gas limit from the callback data. func getCallbackData( - packetDataUnmarshaler porttypes.PacketDataUnmarshaler, - data []byte, srcPortID string, remainingGas, + packetData interface{}, + srcPortID string, + remainingGas, maxGas uint64, callbackKey string, ) (CallbackData, error) { - // unmarshal packet data - packetData, err := packetDataUnmarshaler.UnmarshalPacketData(data) - if err != nil { - return CallbackData{}, errorsmod.Wrap(ErrCannotUnmarshalPacketData, err.Error()) - } - packetDataProvider, ok := packetData.(ibcexported.PacketDataProvider) if !ok { return CallbackData{}, ErrNotPacketDataProvider diff --git a/modules/apps/callbacks/types/callbacks_test.go b/modules/apps/callbacks/types/callbacks_test.go index 4521afc0a41..754118dda90 100644 --- a/modules/apps/callbacks/types/callbacks_test.go +++ b/modules/apps/callbacks/types/callbacks_test.go @@ -3,6 +3,8 @@ package types_test import ( "fmt" + storetypes "cosmossdk.io/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cometbft/cometbft/crypto/secp256k1" @@ -10,6 +12,8 @@ import ( "github.com/cosmos/ibc-go/modules/apps/callbacks/types" "github.com/cosmos/ibc-go/v8/modules/apps/transfer" transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" ibctesting "github.com/cosmos/ibc-go/v8/testing" ibcmock "github.com/cosmos/ibc-go/v8/testing/mock" @@ -277,7 +281,21 @@ func (s *CallbacksTypesTestSuite) TestGetCallbackData() { tc.malleate() - callbackData, err := types.GetCallbackData(packetDataUnmarshaler, packetData, ibcmock.PortID, remainingGas, uint64(1_000_000), callbackKey) + // Set up gas meter for context. + gasMeter := storetypes.NewGasMeter(remainingGas) + ctx := s.chain.GetContext().WithGasMeter(gasMeter) + + packet := channeltypes.NewPacket(packetData, 0, ibcmock.PortID, "", "", "", clienttypes.ZeroHeight(), 0) + + var ( + callbackData types.CallbackData + err error + ) + if callbackKey == types.DestinationCallbackKey { + callbackData, err = types.GetDestCallbackData(ctx, packetDataUnmarshaler, packet, uint64(1_000_000)) + } else { + callbackData, err = types.GetSourceCallbackData(ctx, packetDataUnmarshaler, packet, uint64(1_000_000)) + } expPass := tc.expError == nil if expPass { @@ -315,7 +333,12 @@ func (s *CallbacksTypesTestSuite) TestGetSourceCallbackDataTransfer() { packetUnmarshaler := transfer.IBCModule{} - callbackData, err := types.GetSourceCallbackData(packetUnmarshaler, packetDataBytes, ibcmock.PortID, 2_000_000, 1_000_000) + // Set up gas meter for context. + gasMeter := storetypes.NewGasMeter(2_000_000) + ctx := s.chain.GetContext().WithGasMeter(gasMeter) + + packet := channeltypes.NewPacket(packetDataBytes, 0, ibcmock.PortID, "", "", "", clienttypes.ZeroHeight(), 0) + callbackData, err := types.GetSourceCallbackData(ctx, packetUnmarshaler, packet, 1_000_000) s.Require().NoError(err) s.Require().Equal(expCallbackData, callbackData) } @@ -342,7 +365,11 @@ func (s *CallbacksTypesTestSuite) TestGetDestCallbackDataTransfer() { packetUnmarshaler := transfer.IBCModule{} - callbackData, err := types.GetDestCallbackData(packetUnmarshaler, packetDataBytes, ibcmock.PortID, 2_000_000, 1_000_000) + gasMeter := storetypes.NewGasMeter(2_000_000) + ctx := s.chain.GetContext().WithGasMeter(gasMeter) + + packet := channeltypes.NewPacket(packetDataBytes, 0, ibcmock.PortID, "", "", "", clienttypes.ZeroHeight(), 0) + callbackData, err := types.GetDestCallbackData(ctx, packetUnmarshaler, packet, 1_000_000) s.Require().NoError(err) s.Require().Equal(expCallbackData, callbackData) } diff --git a/modules/apps/callbacks/types/export_test.go b/modules/apps/callbacks/types/export_test.go index 5ba59bed4ca..5b0a32508e3 100644 --- a/modules/apps/callbacks/types/export_test.go +++ b/modules/apps/callbacks/types/export_test.go @@ -1,22 +1,9 @@ package types -import ( - porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" -) - /* This file is to allow for unexported functions to be accessible to the testing package. */ -// GetCallbackData is a wrapper around getCallbackData to allow the function to be directly called in tests. -func GetCallbackData( - packetDataUnmarshaler porttypes.PacketDataUnmarshaler, - packetData []byte, srcPortID string, remainingGas, - maxGas uint64, callbackKey string, -) (CallbackData, error) { - return getCallbackData(packetDataUnmarshaler, packetData, srcPortID, remainingGas, maxGas, callbackKey) -} - // GetCallbackAddress is a wrapper around getCallbackAddress to allow the function to be directly called in tests. func GetCallbackAddress(callbackData map[string]interface{}) string { return getCallbackAddress(callbackData) diff --git a/modules/apps/transfer/ibc_module.go b/modules/apps/transfer/ibc_module.go index 21d3dd45160..a58e777a3e8 100644 --- a/modules/apps/transfer/ibc_module.go +++ b/modules/apps/transfer/ibc_module.go @@ -380,7 +380,7 @@ func (IBCModule) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID string, pr // UnmarshalPacketData attempts to unmarshal the provided packet data bytes // into a FungibleTokenPacketData. This function implements the optional // PacketDataUnmarshaler interface required for ADR 008 support. -func (im IBCModule) UnmarshalPacketData(bz []byte) (interface{}, error) { +func (im IBCModule) UnmarshalPacketData(_ sdk.Context, _, _ string, bz []byte) (interface{}, error) { ftpd, err := im.unmarshalPacketDataBytesToICS20V2(bz) if err != nil { return nil, err diff --git a/modules/apps/transfer/ibc_module_test.go b/modules/apps/transfer/ibc_module_test.go index ddb2dd5b238..46a2302ff8e 100644 --- a/modules/apps/transfer/ibc_module_test.go +++ b/modules/apps/transfer/ibc_module_test.go @@ -604,7 +604,7 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { suite.Run(tc.name, func() { tc.malleate() - packetData, err := transfer.IBCModule{}.UnmarshalPacketData(data) + packetData, err := transfer.IBCModule{}.UnmarshalPacketData(suite.chainA.GetContext(), "", "", data) expPass := tc.expError == nil if expPass { diff --git a/modules/core/05-port/types/module.go b/modules/core/05-port/types/module.go index 23fc5705233..994a90fa17b 100644 --- a/modules/core/05-port/types/module.go +++ b/modules/core/05-port/types/module.go @@ -194,5 +194,5 @@ type Middleware interface { // request the packet data to be unmarshaled by the base application. type PacketDataUnmarshaler interface { // UnmarshalPacketData unmarshals the packet data into a concrete type - UnmarshalPacketData([]byte) (interface{}, error) + UnmarshalPacketData(ctx sdk.Context, portID, channelID string, bz []byte) (interface{}, error) } diff --git a/testing/mock/ibc_module.go b/testing/mock/ibc_module.go index b3bd2432de3..963945f5330 100644 --- a/testing/mock/ibc_module.go +++ b/testing/mock/ibc_module.go @@ -219,7 +219,7 @@ func (im IBCModule) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID string, // UnmarshalPacketData returns the MockPacketData. This function implements the optional // PacketDataUnmarshaler interface required for ADR 008 support. -func (IBCModule) UnmarshalPacketData(bz []byte) (interface{}, error) { +func (IBCModule) UnmarshalPacketData(_ sdk.Context, _, _ string, bz []byte) (interface{}, error) { if reflect.DeepEqual(bz, MockPacketData) { return MockPacketData, nil } From dbcff45015337d734591dc33c0b4886c687b5af8 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 23 May 2024 09:28:04 +0100 Subject: [PATCH 24/42] Refactor packet data unmarshalling to use specific version (#6354) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: specifically unmarshal v1 or v2 without brute force * chore: fix TestPacketDataUnmarshalerInterface test in transfer module * Pass dest values OnRecv, refactor GetExpectedEvents * chore: fixing TestGetCallbackData test * chore: fixed remaining tests in callbacks module * test: simplify callbacks test, revert back to previous behaviour * chore: fix test case name * chore: addressing PR feedback * chore: added docstring for unmarshalPacketDataBytesToICS20V2 --------- Co-authored-by: DimitrisJim Co-authored-by: Colin Axnér <25233464+colin-axner@users.noreply.github.com> --- modules/apps/callbacks/callbacks_test.go | 4 +- modules/apps/callbacks/ibc_middleware_test.go | 43 ++++++- .../apps/callbacks/types/callbacks_test.go | 117 ++++++++---------- modules/apps/callbacks/types/export_test.go | 8 ++ modules/apps/callbacks/types/types_test.go | 11 +- modules/apps/transfer/ibc_module.go | 71 ++++++++--- modules/apps/transfer/ibc_module_test.go | 44 ++++++- 7 files changed, 202 insertions(+), 96 deletions(-) diff --git a/modules/apps/callbacks/callbacks_test.go b/modules/apps/callbacks/callbacks_test.go index ef88db93483..f9874896a22 100644 --- a/modules/apps/callbacks/callbacks_test.go +++ b/modules/apps/callbacks/callbacks_test.go @@ -305,11 +305,11 @@ func GetExpectedEvent( gasMeter := storetypes.NewGasMeter(remainingGas) ctx = ctx.WithGasMeter(gasMeter) - // Mock packet. - packet := channeltypes.NewPacket(data, 0, srcPortID, "", "", "", clienttypes.ZeroHeight(), 0) if callbackType == types.CallbackTypeReceivePacket { + packet := channeltypes.NewPacket(data, seq, "", "", eventPortID, eventChannelID, clienttypes.ZeroHeight(), 0) callbackData, err = types.GetDestCallbackData(ctx, packetDataUnmarshaler, packet, maxCallbackGas) } else { + packet := channeltypes.NewPacket(data, seq, eventPortID, eventChannelID, "", "", clienttypes.ZeroHeight(), 0) callbackData, err = types.GetSourceCallbackData(ctx, packetDataUnmarshaler, packet, maxCallbackGas) } if err != nil { diff --git a/modules/apps/callbacks/ibc_middleware_test.go b/modules/apps/callbacks/ibc_middleware_test.go index 0b487d93611..7c394bcaa9f 100644 --- a/modules/apps/callbacks/ibc_middleware_test.go +++ b/modules/apps/callbacks/ibc_middleware_test.go @@ -971,8 +971,13 @@ func (s *CallbacksTestSuite) TestProcessCallback() { } } -func (s *CallbacksTestSuite) TestUnmarshalPacketData() { +func (s *CallbacksTestSuite) TestUnmarshalPacketDataV1() { s.setupChains() + s.path.EndpointA.ChannelConfig.PortID = ibctesting.TransferPort + s.path.EndpointB.ChannelConfig.PortID = ibctesting.TransferPort + s.path.EndpointA.ChannelConfig.Version = transfertypes.V1 + s.path.EndpointB.ChannelConfig.Version = transfertypes.V1 + s.path.Setup() // We will pass the function call down the transfer stack to the transfer module // transfer stack UnmarshalPacketData call order: callbacks -> fee -> transfer @@ -1006,15 +1011,43 @@ func (s *CallbacksTestSuite) TestUnmarshalPacketData() { portID := s.path.EndpointA.ChannelConfig.PortID channelID := s.path.EndpointA.ChannelID - // Unmarshal ICS20 v1 packet data + // Unmarshal ICS20 v1 packet data into v2 packet data data := expPacketDataICS20V1.GetBytes() packetData, err := unmarshalerStack.UnmarshalPacketData(s.chainA.GetContext(), portID, channelID, data) s.Require().NoError(err) s.Require().Equal(expPacketDataICS20V2, packetData) +} + +func (s *CallbacksTestSuite) TestUnmarshalPacketDataV2() { + s.SetupTransferTest() + + // We will pass the function call down the transfer stack to the transfer module + // transfer stack UnmarshalPacketData call order: callbacks -> fee -> transfer + transferStack, ok := s.chainA.App.GetIBCKeeper().PortKeeper.Route(transfertypes.ModuleName) + s.Require().True(ok) - // Unmarshal ICS20 v1 packet data - data = expPacketDataICS20V2.GetBytes() - packetData, err = unmarshalerStack.UnmarshalPacketData(s.chainA.GetContext(), portID, channelID, data) + unmarshalerStack, ok := transferStack.(types.CallbacksCompatibleModule) + s.Require().True(ok) + + expPacketDataICS20V2 := transfertypes.FungibleTokenPacketDataV2{ + Tokens: []transfertypes.Token{ + { + Denom: ibctesting.TestCoin.Denom, + Amount: ibctesting.TestCoin.Amount.String(), + Trace: nil, + }, + }, + Sender: ibctesting.TestAccAddress, + Receiver: ibctesting.TestAccAddress, + Memo: fmt.Sprintf(`{"src_callback": {"address": "%s"}, "dest_callback": {"address":"%s"}}`, ibctesting.TestAccAddress, ibctesting.TestAccAddress), + } + + portID := s.path.EndpointA.ChannelConfig.PortID + channelID := s.path.EndpointA.ChannelID + + // Unmarshal ICS20 v2 packet data + data := expPacketDataICS20V2.GetBytes() + packetData, err := unmarshalerStack.UnmarshalPacketData(s.chainA.GetContext(), portID, channelID, data) s.Require().NoError(err) s.Require().Equal(expPacketDataICS20V2, packetData) } diff --git a/modules/apps/callbacks/types/callbacks_test.go b/modules/apps/callbacks/types/callbacks_test.go index 754118dda90..be4098f9bc9 100644 --- a/modules/apps/callbacks/types/callbacks_test.go +++ b/modules/apps/callbacks/types/callbacks_test.go @@ -10,23 +10,20 @@ import ( "github.com/cometbft/cometbft/crypto/secp256k1" "github.com/cosmos/ibc-go/modules/apps/callbacks/types" - "github.com/cosmos/ibc-go/v8/modules/apps/transfer" transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" ibctesting "github.com/cosmos/ibc-go/v8/testing" ibcmock "github.com/cosmos/ibc-go/v8/testing/mock" ) func (s *CallbacksTypesTestSuite) TestGetCallbackData() { var ( - sender = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String() - receiver = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String() - packetDataUnmarshaler porttypes.PacketDataUnmarshaler - packetData []byte - remainingGas uint64 - callbackKey string + sender = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String() + receiver = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String() + packetData interface{} + remainingGas uint64 + callbackKey string ) // max gas is 1_000_000 @@ -40,14 +37,13 @@ func (s *CallbacksTypesTestSuite) TestGetCallbackData() { "success: source callback", func() { remainingGas = 2_000_000 - expPacketData := transfertypes.FungibleTokenPacketData{ + packetData = transfertypes.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), Sender: sender, Receiver: receiver, Memo: fmt.Sprintf(`{"src_callback": {"address": "%s"}}`, sender), } - packetData = expPacketData.GetBytes() }, types.CallbackData{ CallbackAddress: sender, @@ -61,15 +57,15 @@ func (s *CallbacksTypesTestSuite) TestGetCallbackData() { "success: destination callback", func() { callbackKey = types.DestinationCallbackKey + remainingGas = 2_000_000 - expPacketData := transfertypes.FungibleTokenPacketData{ + packetData = transfertypes.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), Sender: sender, Receiver: receiver, Memo: fmt.Sprintf(`{"dest_callback": {"address": "%s"}}`, sender), } - packetData = expPacketData.GetBytes() }, types.CallbackData{ CallbackAddress: sender, @@ -83,15 +79,15 @@ func (s *CallbacksTypesTestSuite) TestGetCallbackData() { "success: destination callback with 0 user defined gas limit", func() { callbackKey = types.DestinationCallbackKey + remainingGas = 2_000_000 - expPacketData := transfertypes.FungibleTokenPacketData{ + packetData = transfertypes.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), Sender: sender, Receiver: receiver, Memo: fmt.Sprintf(`{"dest_callback": {"address": "%s", "gas_limit":"0"}}`, sender), } - packetData = expPacketData.GetBytes() }, types.CallbackData{ CallbackAddress: sender, @@ -104,14 +100,13 @@ func (s *CallbacksTypesTestSuite) TestGetCallbackData() { { "success: source callback with gas limit < remaining gas < max gas", func() { - expPacketData := transfertypes.FungibleTokenPacketData{ + packetData = transfertypes.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), Sender: sender, Receiver: receiver, Memo: fmt.Sprintf(`{"src_callback": {"address": "%s", "gas_limit": "50000"}}`, sender), } - packetData = expPacketData.GetBytes() remainingGas = 100_000 }, @@ -127,14 +122,13 @@ func (s *CallbacksTypesTestSuite) TestGetCallbackData() { "success: source callback with remaining gas < gas limit < max gas", func() { remainingGas = 100_000 - expPacketData := transfertypes.FungibleTokenPacketData{ + packetData = transfertypes.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), Sender: sender, Receiver: receiver, Memo: fmt.Sprintf(`{"src_callback": {"address": "%s", "gas_limit": "200000"}}`, sender), } - packetData = expPacketData.GetBytes() }, types.CallbackData{ CallbackAddress: sender, @@ -148,14 +142,13 @@ func (s *CallbacksTypesTestSuite) TestGetCallbackData() { "success: source callback with remaining gas < max gas < gas limit", func() { remainingGas = 100_000 - expPacketData := transfertypes.FungibleTokenPacketData{ + packetData = transfertypes.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), Sender: sender, Receiver: receiver, Memo: fmt.Sprintf(`{"src_callback": {"address": "%s", "gas_limit": "2000000"}}`, sender), } - packetData = expPacketData.GetBytes() }, types.CallbackData{ CallbackAddress: sender, @@ -169,15 +162,15 @@ func (s *CallbacksTypesTestSuite) TestGetCallbackData() { "success: destination callback with remaining gas < max gas < gas limit", func() { callbackKey = types.DestinationCallbackKey + remainingGas = 100_000 - expPacketData := transfertypes.FungibleTokenPacketData{ + packetData = transfertypes.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), Sender: sender, Receiver: receiver, Memo: fmt.Sprintf(`{"dest_callback": {"address": "%s", "gas_limit": "2000000"}}`, sender), } - packetData = expPacketData.GetBytes() }, types.CallbackData{ CallbackAddress: sender, @@ -191,14 +184,13 @@ func (s *CallbacksTypesTestSuite) TestGetCallbackData() { "success: source callback with max gas < remaining gas < gas limit", func() { remainingGas = 2_000_000 - expPacketData := transfertypes.FungibleTokenPacketData{ + packetData = transfertypes.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), Sender: sender, Receiver: receiver, Memo: fmt.Sprintf(`{"src_callback": {"address": "%s", "gas_limit": "3000000"}}`, sender), } - packetData = expPacketData.GetBytes() }, types.CallbackData{ CallbackAddress: sender, @@ -208,19 +200,10 @@ func (s *CallbacksTypesTestSuite) TestGetCallbackData() { }, nil, }, - { - "failure: invalid packet data", - func() { - packetData = []byte("invalid packet data") - }, - types.CallbackData{}, - types.ErrCannotUnmarshalPacketData, - }, { "failure: packet data does not implement PacketDataProvider", func() { packetData = ibcmock.MockPacketData - packetDataUnmarshaler = ibcmock.IBCModule{} }, types.CallbackData{}, types.ErrNotPacketDataProvider, @@ -228,14 +211,13 @@ func (s *CallbacksTypesTestSuite) TestGetCallbackData() { { "failure: empty memo", func() { - expPacketData := transfertypes.FungibleTokenPacketData{ + packetData = transfertypes.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), Sender: sender, Receiver: receiver, Memo: "", } - packetData = expPacketData.GetBytes() }, types.CallbackData{}, types.ErrCallbackKeyNotFound, @@ -243,14 +225,13 @@ func (s *CallbacksTypesTestSuite) TestGetCallbackData() { { "failure: empty address", func() { - expPacketData := transfertypes.FungibleTokenPacketData{ + packetData = transfertypes.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), Sender: sender, Receiver: receiver, Memo: `{"src_callback": {"address": ""}}`, } - packetData = expPacketData.GetBytes() }, types.CallbackData{}, types.ErrCallbackAddressNotFound, @@ -258,14 +239,13 @@ func (s *CallbacksTypesTestSuite) TestGetCallbackData() { { "failure: space address", func() { - expPacketData := transfertypes.FungibleTokenPacketData{ + packetData = transfertypes.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), Sender: sender, Receiver: receiver, Memo: `{"src_callback": {"address": " "}}`, } - packetData = expPacketData.GetBytes() }, types.CallbackData{}, types.ErrCallbackAddressNotFound, @@ -275,27 +255,13 @@ func (s *CallbacksTypesTestSuite) TestGetCallbackData() { for _, tc := range testCases { tc := tc s.Run(tc.name, func() { - callbackKey = types.SourceCallbackKey + s.SetupTest() - packetDataUnmarshaler = transfer.IBCModule{} + callbackKey = types.SourceCallbackKey tc.malleate() - // Set up gas meter for context. - gasMeter := storetypes.NewGasMeter(remainingGas) - ctx := s.chain.GetContext().WithGasMeter(gasMeter) - - packet := channeltypes.NewPacket(packetData, 0, ibcmock.PortID, "", "", "", clienttypes.ZeroHeight(), 0) - - var ( - callbackData types.CallbackData - err error - ) - if callbackKey == types.DestinationCallbackKey { - callbackData, err = types.GetDestCallbackData(ctx, packetDataUnmarshaler, packet, uint64(1_000_000)) - } else { - callbackData, err = types.GetSourceCallbackData(ctx, packetDataUnmarshaler, packet, uint64(1_000_000)) - } + callbackData, err := types.GetCallbackData(packetData, transfertypes.PortID, remainingGas, uint64(1_000_000), callbackKey) expPass := tc.expError == nil if expPass { @@ -312,6 +278,8 @@ func (s *CallbacksTypesTestSuite) TestGetCallbackData() { } func (s *CallbacksTypesTestSuite) TestGetSourceCallbackDataTransfer() { + s.SetupTest() + sender := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String() receiver := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String() @@ -331,19 +299,32 @@ func (s *CallbacksTypesTestSuite) TestGetSourceCallbackDataTransfer() { CommitGasLimit: 1_000_000, } - packetUnmarshaler := transfer.IBCModule{} + s.path.EndpointA.ChannelConfig.Version = transfertypes.V1 + s.path.EndpointA.ChannelConfig.PortID = transfertypes.ModuleName + s.path.EndpointB.ChannelConfig.Version = transfertypes.V1 + s.path.EndpointB.ChannelConfig.PortID = transfertypes.ModuleName + + transferStack, ok := s.chainA.App.GetIBCKeeper().PortKeeper.Route(transfertypes.ModuleName) + s.Require().True(ok) + + packetUnmarshaler, ok := transferStack.(types.CallbacksCompatibleModule) + s.Require().True(ok) + + s.path.Setup() // Set up gas meter for context. gasMeter := storetypes.NewGasMeter(2_000_000) - ctx := s.chain.GetContext().WithGasMeter(gasMeter) + ctx := s.chainA.GetContext().WithGasMeter(gasMeter) - packet := channeltypes.NewPacket(packetDataBytes, 0, ibcmock.PortID, "", "", "", clienttypes.ZeroHeight(), 0) + packet := channeltypes.NewPacket(packetDataBytes, 0, transfertypes.PortID, s.path.EndpointA.ChannelID, transfertypes.PortID, s.path.EndpointB.ChannelID, clienttypes.ZeroHeight(), 0) callbackData, err := types.GetSourceCallbackData(ctx, packetUnmarshaler, packet, 1_000_000) s.Require().NoError(err) s.Require().Equal(expCallbackData, callbackData) } func (s *CallbacksTypesTestSuite) TestGetDestCallbackDataTransfer() { + s.SetupTest() + sender := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String() receiver := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String() @@ -363,12 +344,22 @@ func (s *CallbacksTypesTestSuite) TestGetDestCallbackDataTransfer() { CommitGasLimit: 1_000_000, } - packetUnmarshaler := transfer.IBCModule{} + s.path.EndpointA.ChannelConfig.Version = transfertypes.V1 + s.path.EndpointA.ChannelConfig.PortID = transfertypes.ModuleName + s.path.EndpointB.ChannelConfig.Version = transfertypes.V1 + s.path.EndpointB.ChannelConfig.PortID = transfertypes.ModuleName - gasMeter := storetypes.NewGasMeter(2_000_000) - ctx := s.chain.GetContext().WithGasMeter(gasMeter) + transferStack, ok := s.chainA.App.GetIBCKeeper().PortKeeper.Route(transfertypes.ModuleName) + s.Require().True(ok) + + packetUnmarshaler, ok := transferStack.(types.CallbacksCompatibleModule) + s.Require().True(ok) - packet := channeltypes.NewPacket(packetDataBytes, 0, ibcmock.PortID, "", "", "", clienttypes.ZeroHeight(), 0) + s.path.Setup() + + gasMeter := storetypes.NewGasMeter(2_000_000) + ctx := s.chainA.GetContext().WithGasMeter(gasMeter) + packet := channeltypes.NewPacket(packetDataBytes, 0, transfertypes.PortID, s.path.EndpointA.ChannelID, transfertypes.PortID, s.path.EndpointB.ChannelID, clienttypes.ZeroHeight(), 0) callbackData, err := types.GetDestCallbackData(ctx, packetUnmarshaler, packet, 1_000_000) s.Require().NoError(err) s.Require().Equal(expCallbackData, callbackData) diff --git a/modules/apps/callbacks/types/export_test.go b/modules/apps/callbacks/types/export_test.go index 5b0a32508e3..4cc85624827 100644 --- a/modules/apps/callbacks/types/export_test.go +++ b/modules/apps/callbacks/types/export_test.go @@ -4,6 +4,14 @@ package types This file is to allow for unexported functions to be accessible to the testing package. */ +// GetCallbackData is a wrapper around getCallbackData to allow the function to be directly called in tests. +func GetCallbackData( + packetData interface{}, srcPortID string, remainingGas, + maxGas uint64, callbackKey string, +) (CallbackData, error) { + return getCallbackData(packetData, srcPortID, remainingGas, maxGas, callbackKey) +} + // GetCallbackAddress is a wrapper around getCallbackAddress to allow the function to be directly called in tests. func GetCallbackAddress(callbackData map[string]interface{}) string { return getCallbackAddress(callbackData) diff --git a/modules/apps/callbacks/types/types_test.go b/modules/apps/callbacks/types/types_test.go index 7bb61f95162..5843f972249 100644 --- a/modules/apps/callbacks/types/types_test.go +++ b/modules/apps/callbacks/types/types_test.go @@ -15,12 +15,19 @@ type CallbacksTypesTestSuite struct { coord *ibctesting.Coordinator chain *ibctesting.TestChain + + chainA, chainB *ibctesting.TestChain + + path *ibctesting.Path } // SetupTest creates a coordinator with 1 test chain. -func (s *CallbacksTypesTestSuite) SetupSuite() { - s.coord = ibctesting.NewCoordinator(s.T(), 1) +func (s *CallbacksTypesTestSuite) SetupTest() { + s.coord = ibctesting.NewCoordinator(s.T(), 3) s.chain = s.coord.GetChain(ibctesting.GetChainID(1)) + s.chainA = s.coord.GetChain(ibctesting.GetChainID(2)) + s.chainB = s.coord.GetChain(ibctesting.GetChainID(3)) + s.path = ibctesting.NewPath(s.chainA, s.chainB) } func TestCallbacksTypesTestSuite(t *testing.T) { diff --git a/modules/apps/transfer/ibc_module.go b/modules/apps/transfer/ibc_module.go index a58e777a3e8..c87591e6c9f 100644 --- a/modules/apps/transfer/ibc_module.go +++ b/modules/apps/transfer/ibc_module.go @@ -174,25 +174,35 @@ func (IBCModule) OnChanCloseConfirm( return nil } -func (IBCModule) unmarshalPacketDataBytesToICS20V2(bz []byte) (types.FungibleTokenPacketDataV2, error) { - // TODO: remove support for this function parsing v1 packet data - // TODO: explicit check for packet data type against app version - - var datav1 types.FungibleTokenPacketData - if err := json.Unmarshal(bz, &datav1); err == nil { - if len(datav1.Denom) != 0 { - return convertinternal.PacketDataV1ToV2(datav1), nil +// unmarshalPacketDataBytesToICS20V2 attempts to unmarshal the provided packet data bytes into a FungibleTokenPacketDataV2. +// The version of ics20 should be provided and should be either ics20-1 or ics20-2. +func (IBCModule) unmarshalPacketDataBytesToICS20V2(bz []byte, ics20Version string) (types.FungibleTokenPacketDataV2, error) { + switch ics20Version { + case types.V1: + var datav1 types.FungibleTokenPacketData + if err := json.Unmarshal(bz, &datav1); err != nil { + return types.FungibleTokenPacketDataV2{}, errorsmod.Wrapf(ibcerrors.ErrInvalidType, "cannot unmarshal ICS20-V2 transfer packet data: %s", err.Error()) } - } - var data types.FungibleTokenPacketDataV2 - if err := json.Unmarshal(bz, &data); err == nil { - if len(data.Tokens) != 0 { - return data, nil + if err := datav1.ValidateBasic(); err != nil { + return types.FungibleTokenPacketDataV2{}, err + } + + return convertinternal.PacketDataV1ToV2(datav1), nil + case types.V2: + var datav2 types.FungibleTokenPacketDataV2 + if err := json.Unmarshal(bz, &datav2); err != nil { + return types.FungibleTokenPacketDataV2{}, errorsmod.Wrapf(ibcerrors.ErrInvalidType, "cannot unmarshal ICS20-V2 transfer packet data: %s", err.Error()) } - } - return types.FungibleTokenPacketDataV2{}, errorsmod.Wrapf(ibcerrors.ErrInvalidType, "cannot unmarshal ICS-20 transfer packet data") + if err := datav2.ValidateBasic(); err != nil { + return types.FungibleTokenPacketDataV2{}, err + } + + return datav2, nil + default: + return types.FungibleTokenPacketDataV2{}, errorsmod.Wrap(types.ErrInvalidVersion, ics20Version) + } } // OnRecvPacket implements the IBCModule interface. A successful acknowledgement @@ -205,8 +215,14 @@ func (im IBCModule) OnRecvPacket( ) ibcexported.Acknowledgement { ack := channeltypes.NewResultAcknowledgement([]byte{byte(1)}) - data, ackErr := im.unmarshalPacketDataBytesToICS20V2(packet.GetData()) + ics20Version, found := im.keeper.GetICS4Wrapper().GetAppVersion(ctx, packet.DestinationPort, packet.DestinationChannel) + if !found { + return channeltypes.NewErrorAcknowledgement(errorsmod.Wrapf(ibcerrors.ErrNotFound, "app version not found for port %s and channel %s", packet.DestinationPort, packet.DestinationChannel)) + } + + data, ackErr := im.unmarshalPacketDataBytesToICS20V2(packet.GetData(), ics20Version) if ackErr != nil { + ackErr = errorsmod.Wrapf(ibcerrors.ErrInvalidType, ackErr.Error()) im.keeper.Logger(ctx).Error(fmt.Sprintf("%s sequence %d", ackErr.Error(), packet.Sequence)) ack = channeltypes.NewErrorAcknowledgement(ackErr) } @@ -261,7 +277,12 @@ func (im IBCModule) OnAcknowledgementPacket( return errorsmod.Wrapf(ibcerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet acknowledgement: %v", err) } - data, err := im.unmarshalPacketDataBytesToICS20V2(packet.GetData()) + ics20Version, found := im.keeper.GetICS4Wrapper().GetAppVersion(ctx, packet.SourcePort, packet.SourceChannel) + if !found { + return errorsmod.Wrapf(ibcerrors.ErrNotFound, "app version not found for port %s and channel %s", packet.SourcePort, packet.SourceChannel) + } + + data, err := im.unmarshalPacketDataBytesToICS20V2(packet.GetData(), ics20Version) if err != nil { return err } @@ -311,7 +332,12 @@ func (im IBCModule) OnTimeoutPacket( packet channeltypes.Packet, relayer sdk.AccAddress, ) error { - data, err := im.unmarshalPacketDataBytesToICS20V2(packet.GetData()) + ics20Version, found := im.keeper.GetICS4Wrapper().GetAppVersion(ctx, packet.SourcePort, packet.SourceChannel) + if !found { + return errorsmod.Wrapf(ibcerrors.ErrNotFound, "app version not found for port %s and channel %s", packet.SourcePort, packet.SourceChannel) + } + + data, err := im.unmarshalPacketDataBytesToICS20V2(packet.GetData(), ics20Version) if err != nil { return err } @@ -380,8 +406,13 @@ func (IBCModule) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID string, pr // UnmarshalPacketData attempts to unmarshal the provided packet data bytes // into a FungibleTokenPacketData. This function implements the optional // PacketDataUnmarshaler interface required for ADR 008 support. -func (im IBCModule) UnmarshalPacketData(_ sdk.Context, _, _ string, bz []byte) (interface{}, error) { - ftpd, err := im.unmarshalPacketDataBytesToICS20V2(bz) +func (im IBCModule) UnmarshalPacketData(ctx sdk.Context, portID, channelID string, bz []byte) (interface{}, error) { + ics20Version, found := im.keeper.GetICS4Wrapper().GetAppVersion(ctx, portID, channelID) + if !found { + return nil, errorsmod.Wrapf(ibcerrors.ErrNotFound, "app version not found for port %s and channel %s", portID, channelID) + } + + ftpd, err := im.unmarshalPacketDataBytesToICS20V2(bz, ics20Version) if err != nil { return nil, err } diff --git a/modules/apps/transfer/ibc_module_test.go b/modules/apps/transfer/ibc_module_test.go index 46a2302ff8e..e52b04fc7da 100644 --- a/modules/apps/transfer/ibc_module_test.go +++ b/modules/apps/transfer/ibc_module_test.go @@ -496,6 +496,7 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { sender = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String() receiver = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String() + path *ibctesting.Path data []byte initialPacketData interface{} ) @@ -508,6 +509,7 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { { "success: valid packet data single denom -> multidenom conversion with memo", func() { + path.EndpointA.ChannelConfig.Version = types.V1 initialPacketData = types.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), @@ -523,6 +525,7 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { { "success: valid packet data single denom -> multidenom conversion without memo", func() { + path.EndpointA.ChannelConfig.Version = types.V1 initialPacketData = types.FungibleTokenPacketData{ Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), @@ -538,6 +541,7 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { { "success: valid packet data single denom with trace -> multidenom conversion with trace", func() { + path.EndpointA.ChannelConfig.Version = types.V1 initialPacketData = types.FungibleTokenPacketData{ Denom: "transfer/channel-0/atom", Amount: ibctesting.TestCoin.Amount.String(), @@ -571,14 +575,15 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { nil, }, { - "success: valid packet data multidenom without memo", + "success: valid packet data multidenom nil trace", func() { + path.EndpointA.ChannelConfig.Version = types.V2 initialPacketData = types.FungibleTokenPacketDataV2{ Tokens: []types.Token{ { Denom: ibctesting.TestCoin.Denom, Amount: ibctesting.TestCoin.Amount.String(), - Trace: []string{""}, + Trace: nil, }, }, Sender: sender, @@ -590,21 +595,52 @@ func (suite *TransferTestSuite) TestPacketDataUnmarshalerInterface() { }, nil, }, + { + "failure: invalid token trace", + func() { + path.EndpointA.ChannelConfig.Version = types.V2 + initialPacketData = types.FungibleTokenPacketDataV2{ + Tokens: []types.Token{ + { + Denom: ibctesting.TestCoin.Denom, + Amount: ibctesting.TestCoin.Amount.String(), + Trace: []string{""}, + }, + }, + Sender: sender, + Receiver: receiver, + Memo: "", + } + + data = initialPacketData.(types.FungibleTokenPacketDataV2).GetBytes() + }, + errors.New("trace info must come in pairs of port and channel identifiers"), + }, { "failure: invalid packet data", func() { data = []byte("invalid packet data") }, - errors.New("cannot unmarshal ICS-20 transfer packet data"), + errors.New("cannot unmarshal ICS20-V2 transfer packet data"), }, } for _, tc := range testCases { tc := tc suite.Run(tc.name, func() { + path = ibctesting.NewTransferPath(suite.chainA, suite.chainB) + tc.malleate() - packetData, err := transfer.IBCModule{}.UnmarshalPacketData(suite.chainA.GetContext(), "", "", data) + path.Setup() + + transferStack, ok := suite.chainA.App.GetIBCKeeper().PortKeeper.Route(types.ModuleName) + suite.Require().True(ok) + + unmarshalerStack, ok := transferStack.(porttypes.PacketDataUnmarshaler) + suite.Require().True(ok) + + packetData, err := unmarshalerStack.UnmarshalPacketData(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, data) expPass := tc.expError == nil if expPass { From f19a1458d766ec7947f940cb8518d6251dfbcf44 Mon Sep 17 00:00:00 2001 From: chatton Date: Thu, 23 May 2024 09:59:23 +0100 Subject: [PATCH 25/42] chore: fixing tests --- modules/apps/transfer/keeper/relay_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index c9359fee456..0f7e8a70997 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -996,7 +996,7 @@ func (suite *KeeperTestSuite) TestPacketForwardsCompatibility() { func() { packetData = []byte("invalid packet data") }, - ibcerrors.ErrUnknownRequest, + ibcerrors.ErrInvalidType, }, { "failure: missing field", @@ -1004,7 +1004,7 @@ func (suite *KeeperTestSuite) TestPacketForwardsCompatibility() { jsonString := fmt.Sprintf(`{"amount":"100","sender":%s","receiver":"%s"}`, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String()) packetData = []byte(jsonString) }, - ibcerrors.ErrUnknownRequest, + ibcerrors.ErrInvalidType, }, } @@ -1015,6 +1015,8 @@ func (suite *KeeperTestSuite) TestPacketForwardsCompatibility() { packetData = nil path := ibctesting.NewTransferPath(suite.chainA, suite.chainB) + path.EndpointA.ChannelConfig.Version = types.V1 + path.EndpointB.ChannelConfig.Version = types.V1 path.Setup() tc.malleate() From d4b06c8d6aa2352e1adbd90aae9a868276469916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Thu, 23 May 2024 13:04:48 +0200 Subject: [PATCH 26/42] imp: self review comments for ics20-v2 (#6360) * refactor: address various self review comments * revert: unnecessary change * lint --- CHANGELOG.md | 1 + docs/docs/05-migrations/13-v8-to-v9.md | 1 + e2e/testsuite/testsuite.go | 2 +- modules/apps/transfer/types/keys.go | 4 +- modules/apps/transfer/types/token_test.go | 82 +++++++++++++++++++++++ 5 files changed, 87 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9299281e19..00a5a5ba760 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (core) [\#6138](https://github.com/cosmos/ibc-go/pull/6138) Remove `Router` reference from IBC core keeper and use instead the router on the existing `PortKeeper` reference. * (core/04-channel) [\#6023](https://github.com/cosmos/ibc-go/pull/6023) Remove emission of non-hexlified event attributes `packet_data` and `packet_ack`. * (core) [\#6320](https://github.com/cosmos/ibc-go/pull/6320) Remove unnecessary `Proof` interface from `exported` package. +* (core/05-port) [\#6341](https://github.com/cosmos/ibc-go/pull/6341) Modify `UnmarshalPacketData` interface to take in the context, portID, and channelID. This allows for packet data's to be unmarshaled based on the channel version. ### State Machine Breaking diff --git a/docs/docs/05-migrations/13-v8-to-v9.md b/docs/docs/05-migrations/13-v8-to-v9.md index afbf6a0e98c..bd67a09515d 100644 --- a/docs/docs/05-migrations/13-v8-to-v9.md +++ b/docs/docs/05-migrations/13-v8-to-v9.md @@ -66,6 +66,7 @@ func AssertEvents( ### IBC core +- `UnmarshalPacketData` now takes in the context, portID, and channelID. This allows the packet data to be unmarshaled based on the channel version. - `Router` reference has been removed from IBC core keeper: [#6138](https://github.com/cosmos/ibc-go/pull/6138) ### ICS27 - Interchain Accounts diff --git a/e2e/testsuite/testsuite.go b/e2e/testsuite/testsuite.go index 221c523f186..4552dea20ee 100644 --- a/e2e/testsuite/testsuite.go +++ b/e2e/testsuite/testsuite.go @@ -154,8 +154,8 @@ func (s *E2ETestSuite) ConfigureRelayer(ctx context.Context, chainA, chainB ibc. pathName := s.generatePathName() channelOptions := ibc.DefaultChannelOpts() - // TODO: better way to do this. // For now, set the version to the latest transfer module version + // DefaultChannelOpts uses V1 at the moment channelOptions.Version = transfertypes.V2 if channelOpts != nil { diff --git a/modules/apps/transfer/types/keys.go b/modules/apps/transfer/types/keys.go index 5e1fa0d70e6..f8faa1336fe 100644 --- a/modules/apps/transfer/types/keys.go +++ b/modules/apps/transfer/types/keys.go @@ -38,8 +38,8 @@ const ( // V1 defines first version of the IBC transfer module V1 = "ics20-1" - // V2 defines the current version the IBC transfer - // module supports + // V2 defines the transfer version which introduces multidenom support + // through the FungibleTokenPacketDataV2. It is the latest version. V2 = "ics20-2" // escrowAddressVersion should remain as ics20-1 to avoid the address changing. diff --git a/modules/apps/transfer/types/token_test.go b/modules/apps/transfer/types/token_test.go index afc008b6ed1..027405b582e 100644 --- a/modules/apps/transfer/types/token_test.go +++ b/modules/apps/transfer/types/token_test.go @@ -149,3 +149,85 @@ func TestValidate(t *testing.T) { }) } } + +func TestTokens_String(t *testing.T) { + cases := []struct { + name string + input Tokens + expected string + }{ + { + "empty tokens", + Tokens{}, + "", + }, + { + "single token, no trace", + Tokens{ + Token{ + Denom: "tree", + Amount: "1", + Trace: []string{}, + }, + }, + `denom:"tree" amount:"1" `, + }, + { + "single token with trace", + Tokens{ + Token{ + Denom: "tree", + Amount: "1", + Trace: []string{"portid/channelid"}, + }, + }, + `denom:"tree" amount:"1" trace:"portid/channelid" `, + }, + { + "multiple tokens, no trace", + Tokens{ + Token{ + Denom: "tree", + Amount: "1", + Trace: []string{}, + }, + Token{ + Denom: "gas", + Amount: "2", + Trace: []string{}, + }, + Token{ + Denom: "mineral", + Amount: "3", + Trace: []string{}, + }, + }, + `denom:"tree" amount:"1" ,denom:"gas" amount:"2" ,denom:"mineral" amount:"3" `, + }, + { + "multiple tokens, trace and no trace", + Tokens{ + Token{ + Denom: "tree", + Amount: "1", + Trace: []string{}, + }, + Token{ + Denom: "gas", + Amount: "2", + Trace: []string{"portid/channelid"}, + }, + Token{ + Denom: "mineral", + Amount: "3", + Trace: []string{"portid/channelid", "transfer/channel-52"}, + }, + }, + `denom:"tree" amount:"1" ,denom:"gas" amount:"2" trace:"portid/channelid" ,denom:"mineral" amount:"3" trace:"portid/channelid" trace:"transfer/channel-52" `, + }, + } + + for _, tt := range cases { + require.Equal(t, tt.expected, tt.input.String()) + } +} From a9391a4fcf1b6b644835d86c50399addbbe0d117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Thu, 23 May 2024 16:12:29 +0200 Subject: [PATCH 27/42] imp: self review on ics20-v2 part 2 (#6364) * refactor: apply review suggestions * imp: additional updates * refactor: make ValidateIBCDenom private * Update modules/apps/transfer/types/msgs.go Co-authored-by: Cian Hatton * apply review suggestions --------- Co-authored-by: Cian Hatton --- modules/apps/transfer/keeper/msg_server.go | 22 +++---- modules/apps/transfer/types/export_test.go | 6 ++ modules/apps/transfer/types/msgs.go | 62 +++++++++---------- modules/apps/transfer/types/packet.go | 9 --- modules/apps/transfer/types/token.go | 19 ++++-- modules/apps/transfer/types/token_test.go | 25 +++----- modules/apps/transfer/types/trace.go | 4 +- .../transfer/types/transfer_authorization.go | 8 +-- 8 files changed, 74 insertions(+), 81 deletions(-) create mode 100644 modules/apps/transfer/types/export_test.go diff --git a/modules/apps/transfer/keeper/msg_server.go b/modules/apps/transfer/keeper/msg_server.go index e23672facb3..eaac2fb3ae9 100644 --- a/modules/apps/transfer/keeper/msg_server.go +++ b/modules/apps/transfer/keeper/msg_server.go @@ -26,23 +26,21 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types. return nil, err } - // tokens will always be an array, but may contain a single element - // if the ics20-1 token field is populated, and the ics20-2 array is empty. - tokens := msg.GetTokens() + coins := msg.GetCoins() appVersion, found := k.ics4Wrapper.GetAppVersion(ctx, msg.SourcePort, msg.SourceChannel) if !found { return nil, errorsmod.Wrapf(ibcerrors.ErrInvalidRequest, "application version not found for source port: %s and source channel: %s", msg.SourcePort, msg.SourceChannel) } - // ics20-1 only supports a single token, so if that is the current version, we must only process a single token. - if appVersion == types.V1 && len(tokens) > 1 { - return nil, errorsmod.Wrapf(ibcerrors.ErrInvalidRequest, "cannot transfer multiple tokens with ics20-1") + // ics20-1 only supports a single coin, so if that is the current version, we must only process a single coin. + if appVersion == types.V1 && len(coins) > 1 { + return nil, errorsmod.Wrapf(ibcerrors.ErrInvalidRequest, "cannot transfer multiple coins with ics20-1") } - for _, token := range tokens { - if !k.bankKeeper.IsSendEnabledCoin(ctx, token) { - return nil, errorsmod.Wrapf(types.ErrSendDisabled, "transfers are currently disabled for %s", token.Denom) + for _, coin := range coins { + if !k.bankKeeper.IsSendEnabledCoin(ctx, coin) { + return nil, errorsmod.Wrapf(types.ErrSendDisabled, "transfers are currently disabled for %s", coin.Denom) } } @@ -51,7 +49,7 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types. } sequence, err := k.sendTransfer( - ctx, msg.SourcePort, msg.SourceChannel, tokens, sender, msg.Receiver, msg.TimeoutHeight, msg.TimeoutTimestamp, + ctx, msg.SourcePort, msg.SourceChannel, coins, sender, msg.Receiver, msg.TimeoutHeight, msg.TimeoutTimestamp, msg.Memo) if err != nil { return nil, err @@ -62,7 +60,7 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types. types.EventTypeTransfer, sdk.NewAttribute(types.AttributeKeySender, msg.Sender), sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver), - sdk.NewAttribute(types.AttributeKeyTokens, tokens.String()), + sdk.NewAttribute(types.AttributeKeyTokens, coins.String()), sdk.NewAttribute(types.AttributeKeyMemo, msg.Memo), ), sdk.NewEvent( @@ -71,7 +69,7 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types. ), }) - k.Logger(ctx).Info("IBC fungible token transfer", "tokens", tokens, "sender", msg.Sender, "receiver", msg.Receiver) + k.Logger(ctx).Info("IBC fungible token transfer", "tokens", coins, "sender", msg.Sender, "receiver", msg.Receiver) return &types.MsgTransferResponse{Sequence: sequence}, nil } diff --git a/modules/apps/transfer/types/export_test.go b/modules/apps/transfer/types/export_test.go new file mode 100644 index 00000000000..65e136757fe --- /dev/null +++ b/modules/apps/transfer/types/export_test.go @@ -0,0 +1,6 @@ +package types + +// ValidateIBCDenom is a wrapper around validateIBCDenom for testing purposes. +func ValidateIBCDenom(denom string) error { + return validateIBCDenom(denom) +} diff --git a/modules/apps/transfer/types/msgs.go b/modules/apps/transfer/types/msgs.go index 85e10c4db31..fd28b8778e5 100644 --- a/modules/apps/transfer/types/msgs.go +++ b/modules/apps/transfer/types/msgs.go @@ -74,12 +74,12 @@ func (msg MsgTransfer) ValidateBasic() error { return errorsmod.Wrap(err, "invalid source channel ID") } - if len(msg.Tokens) == 0 && !isValidToken(msg.Token) { - return errorsmod.Wrap(ErrInvalidAmount, "either token or token array must be filled") + if len(msg.Tokens) == 0 && !isValidIBCCoin(msg.Token) { + return errorsmod.Wrap(ibcerrors.ErrInvalidCoins, "either token or token array must be filled") } - if len(msg.Tokens) != 0 && isValidToken(msg.Token) { - return errorsmod.Wrap(ErrInvalidAmount, "cannot fill both token and token array") + if len(msg.Tokens) != 0 && isValidIBCCoin(msg.Token) { + return errorsmod.Wrap(ibcerrors.ErrInvalidCoins, "cannot fill both token and token array") } if len(msg.Tokens) > MaximumTokensLength { @@ -100,47 +100,45 @@ func (msg MsgTransfer) ValidateBasic() error { return errorsmod.Wrapf(ErrInvalidMemo, "memo must not exceed %d bytes", MaximumMemoLength) } - for _, token := range msg.GetTokens() { - if !isValidToken(token) { - return errorsmod.Wrap(ibcerrors.ErrInvalidCoins, token.String()) - } - if err := ValidateIBCDenom(token.GetDenom()); err != nil { - return errorsmod.Wrap(ibcerrors.ErrInvalidCoins, token.Denom) + for _, coin := range msg.GetCoins() { + if err := validateIBCCoin(coin); err != nil { + return errorsmod.Wrap(err, coin.String()) } } return nil } -// GetTokens returns the tokens which will be transferred. -func (msg MsgTransfer) GetTokens() sdk.Coins { - tokens := msg.Tokens - if isValidToken(msg.Token) { - tokens = []sdk.Coin{msg.Token} +// GetCoins returns the tokens which will be transferred. +// If MsgTransfer is populated in the Token field, only that field +// will be returned in the coin array. +func (msg MsgTransfer) GetCoins() sdk.Coins { + coins := msg.Tokens + if isValidIBCCoin(msg.Token) { + coins = []sdk.Coin{msg.Token} } - return tokens + return coins } -// isValidToken returns true if the token provided is valid, +// isValidIBCCoin returns true if the token provided is valid, // and should be used to transfer tokens. -// this function is used in case the user constructs a sdk.Coin literal -// instead of using the construction function. -func isValidToken(coin sdk.Coin) bool { - if coin.IsNil() { - return false - } +func isValidIBCCoin(coin sdk.Coin) bool { + return validateIBCCoin(coin) == nil +} - if strings.TrimSpace(coin.Denom) == "" { - return false +// isValidIBCCoin returns true if the token provided is valid, +// and should be used to transfer tokens. The token must +// have a positive amount. +func validateIBCCoin(coin sdk.Coin) error { + if err := coin.Validate(); err != nil { + return err } - - if coin.Amount.IsZero() { - return false + if !coin.IsPositive() { + return errorsmod.Wrap(ErrInvalidAmount, "amount must be positive") } - - if coin.Amount.IsNegative() { - return false + if err := validateIBCDenom(coin.GetDenom()); err != nil { + return err } - return true + return nil } diff --git a/modules/apps/transfer/types/packet.go b/modules/apps/transfer/types/packet.go index 81e46c72a53..649bc7cddad 100644 --- a/modules/apps/transfer/types/packet.go +++ b/modules/apps/transfer/types/packet.go @@ -128,15 +128,6 @@ func (ftpd FungibleTokenPacketDataV2) ValidateBasic() error { } for _, token := range ftpd.Tokens { - amount, ok := sdkmath.NewIntFromString(token.Amount) - if !ok { - return errorsmod.Wrapf(ErrInvalidAmount, "unable to parse transfer amount (%s) into math.Int", token.Amount) - } - - if !amount.IsPositive() { - return errorsmod.Wrapf(ErrInvalidAmount, "amount must be strictly positive: got %d", amount) - } - if err := token.Validate(); err != nil { return err } diff --git a/modules/apps/transfer/types/token.go b/modules/apps/transfer/types/token.go index a91dbb97792..a9d822d58e5 100644 --- a/modules/apps/transfer/types/token.go +++ b/modules/apps/transfer/types/token.go @@ -4,16 +4,24 @@ import ( "strings" errorsmod "cosmossdk.io/errors" - - sdk "github.com/cosmos/cosmos-sdk/types" + sdkmath "cosmossdk.io/math" denominternal "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/denom" ) // Validate validates a token denomination and trace identifiers. func (t Token) Validate() error { - if err := sdk.ValidateDenom(t.Denom); err != nil { - return errorsmod.Wrap(ErrInvalidDenomForTransfer, err.Error()) + if strings.TrimSpace(t.Denom) == "" { + return errorsmod.Wrap(ErrInvalidDenomForTransfer, "denom cannot be empty") + } + + amount, ok := sdkmath.NewIntFromString(t.Amount) + if !ok { + return errorsmod.Wrapf(ErrInvalidAmount, "unable to parse transfer amount (%s) into math.Int", t.Amount) + } + + if !amount.IsPositive() { + return errorsmod.Wrapf(ErrInvalidAmount, "amount must be strictly positive: got %d", amount) } if len(t.Trace) == 0 { @@ -30,8 +38,7 @@ func (t Token) Validate() error { // tracePath + "/" + baseDenom // If there exists no trace then the base denomination is returned. func (t Token) GetFullDenomPath() string { - trace := strings.Join(t.Trace, "/") - if len(trace) == 0 { + if len(t.Trace) == 0 { return t.Denom } diff --git a/modules/apps/transfer/types/token_test.go b/modules/apps/transfer/types/token_test.go index 027405b582e..7ae88757b90 100644 --- a/modules/apps/transfer/types/token_test.go +++ b/modules/apps/transfer/types/token_test.go @@ -58,22 +58,6 @@ func TestGetFullDenomPath(t *testing.T) { ), denom, }, - { - "empty string trace", - NewFungibleTokenPacketDataV2( - []Token{ - { - Denom: denom, - Amount: amount, - Trace: []string{""}, - }, - }, - sender, - receiver, - "", - ), - denom, - }, } for _, tc := range testCases { @@ -135,6 +119,15 @@ func TestValidate(t *testing.T) { }, fmt.Errorf("trace info must come in pairs of port and channel identifiers '{portID}/{channelID}', got the identifiers: [transfer channel-1 randomport]"), }, + { + "failure: empty identifier in trace", + Token{ + Denom: "uatom", + Amount: amount, + Trace: []string{""}, + }, + fmt.Errorf("trace info must come in pairs of port and channel identifiers '{portID}/{channelID}', got the identifiers: "), + }, } for _, tc := range testCases { diff --git a/modules/apps/transfer/types/trace.go b/modules/apps/transfer/types/trace.go index ab4e7bbce72..0ac37b56645 100644 --- a/modules/apps/transfer/types/trace.go +++ b/modules/apps/transfer/types/trace.go @@ -163,11 +163,11 @@ func ValidatePrefixedDenom(denom string) error { return denominternal.ValidateTraceIdentifiers(identifiers) } -// ValidateIBCDenom validates that the given denomination is either: +// validateIBCDenom validates that the given denomination is either: // // - A valid base denomination (eg: 'uatom' or 'gamm/pool/1' as in https://github.com/cosmos/ibc-go/issues/894) // - A valid fungible token representation (i.e 'ibc/{hash}') per ADR 001 https://github.com/cosmos/ibc-go/blob/main/docs/architecture/adr-001-coin-source-tracing.md -func ValidateIBCDenom(denom string) error { +func validateIBCDenom(denom string) error { if err := sdk.ValidateDenom(denom); err != nil { return err } diff --git a/modules/apps/transfer/types/transfer_authorization.go b/modules/apps/transfer/types/transfer_authorization.go index ea3c42a9994..333519a86a2 100644 --- a/modules/apps/transfer/types/transfer_authorization.go +++ b/modules/apps/transfer/types/transfer_authorization.go @@ -68,16 +68,16 @@ func (a TransferAuthorization) Accept(goCtx context.Context, msg proto.Message) allocationModified := false // update spend limit for each token in the MsgTransfer - for _, token := range msgTransfer.GetTokens() { + for _, coin := range msgTransfer.GetCoins() { // If the spend limit is set to the MaxUint256 sentinel value, do not subtract the amount from the spend limit. // if there is no unlimited spend, then we need to subtract the amount from the spend limit to get the limit left - if allocation.SpendLimit.AmountOf(token.Denom).Equal(UnboundedSpendLimit()) { + if allocation.SpendLimit.AmountOf(coin.Denom).Equal(UnboundedSpendLimit()) { continue } - limitLeft, isNegative := a.Allocations[index].SpendLimit.SafeSub(token) + limitLeft, isNegative := a.Allocations[index].SpendLimit.SafeSub(coin) if isNegative { - return authz.AcceptResponse{}, errorsmod.Wrapf(ibcerrors.ErrInsufficientFunds, "requested amount of token %s is more than spend limit", token.Denom) + return authz.AcceptResponse{}, errorsmod.Wrapf(ibcerrors.ErrInsufficientFunds, "requested amount of token %s is more than spend limit", coin.Denom) } allocationModified = true From 575403ec8d45fdaeb02e60b973201c2f078f6f59 Mon Sep 17 00:00:00 2001 From: DimitrisJim Date: Thu, 23 May 2024 17:49:03 +0300 Subject: [PATCH 28/42] chore: move functions from internal/denom back to trace.go (#6368) * chore: move functions from internal/denom to trace.go * lint * lint: the comeback --- modules/apps/transfer/internal/denom/denom.go | 61 ------------------- modules/apps/transfer/types/token.go | 4 +- modules/apps/transfer/types/trace.go | 61 +++++++++++++++++-- 3 files changed, 57 insertions(+), 69 deletions(-) delete mode 100644 modules/apps/transfer/internal/denom/denom.go diff --git a/modules/apps/transfer/internal/denom/denom.go b/modules/apps/transfer/internal/denom/denom.go deleted file mode 100644 index e6b9d7b7680..00000000000 --- a/modules/apps/transfer/internal/denom/denom.go +++ /dev/null @@ -1,61 +0,0 @@ -package denom - -import ( - "fmt" - "strings" - - errorsmod "cosmossdk.io/errors" - - channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v8/modules/core/24-host" -) - -// ExtractPathAndBaseFromFullDenom returns the trace path and the base denom from -// the elements that constitute the complete denom. -func ExtractPathAndBaseFromFullDenom(fullDenomItems []string) ([]string, string) { - var ( - pathSlice []string - baseDenomSlice []string - ) - - length := len(fullDenomItems) - for i := 0; i < length; i += 2 { - // The IBC specification does not guarantee the expected format of the - // destination port or destination channel identifier. A short term solution - // to determine base denomination is to expect the channel identifier to be the - // one ibc-go specifies. A longer term solution is to separate the path and base - // denomination in the ICS20 packet. If an intermediate hop prefixes the full denom - // with a channel identifier format different from our own, the base denomination - // will be incorrectly parsed, but the token will continue to be treated correctly - // as an IBC denomination. The hash used to store the token internally on our chain - // will be the same value as the base denomination being correctly parsed. - if i < length-1 && length > 2 && channeltypes.IsValidChannelID(fullDenomItems[i+1]) { - pathSlice = append(pathSlice, fullDenomItems[i], fullDenomItems[i+1]) - } else { - baseDenomSlice = fullDenomItems[i:] - break - } - } - - baseDenom := strings.Join(baseDenomSlice, "/") - - return pathSlice, baseDenom -} - -// ValidateTraceIdentifiers validates the correctness of the trace associated with a particular base denom. -func ValidateTraceIdentifiers(identifiers []string) error { - if len(identifiers) == 0 || len(identifiers)%2 != 0 { - return fmt.Errorf("trace info must come in pairs of port and channel identifiers '{portID}/{channelID}', got the identifiers: %s", identifiers) - } - - // validate correctness of port and channel identifiers - for i := 0; i < len(identifiers); i += 2 { - if err := host.PortIdentifierValidator(identifiers[i]); err != nil { - return errorsmod.Wrapf(err, "invalid port ID at position %d", i) - } - if err := host.ChannelIdentifierValidator(identifiers[i+1]); err != nil { - return errorsmod.Wrapf(err, "invalid channel ID at position %d", i) - } - } - return nil -} diff --git a/modules/apps/transfer/types/token.go b/modules/apps/transfer/types/token.go index a9d822d58e5..4fe2928489d 100644 --- a/modules/apps/transfer/types/token.go +++ b/modules/apps/transfer/types/token.go @@ -5,8 +5,6 @@ import ( errorsmod "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" - - denominternal "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/denom" ) // Validate validates a token denomination and trace identifiers. @@ -31,7 +29,7 @@ func (t Token) Validate() error { trace := strings.Join(t.Trace, "/") identifiers := strings.Split(trace, "/") - return denominternal.ValidateTraceIdentifiers(identifiers) + return validateTraceIdentifiers(identifiers) } // GetFullDenomPath returns the full denomination according to the ICS20 specification: diff --git a/modules/apps/transfer/types/trace.go b/modules/apps/transfer/types/trace.go index 0ac37b56645..3287ffc1796 100644 --- a/modules/apps/transfer/types/trace.go +++ b/modules/apps/transfer/types/trace.go @@ -14,7 +14,8 @@ import ( cmtbytes "github.com/cometbft/cometbft/libs/bytes" cmttypes "github.com/cometbft/cometbft/types" - denominternal "github.com/cosmos/ibc-go/v8/modules/apps/transfer/internal/denom" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v8/modules/core/24-host" ) // ParseDenomTrace parses a string with the ibc prefix (denom trace) and the base denomination @@ -37,7 +38,7 @@ func ParseDenomTrace(rawDenom string) DenomTrace { } } - pathSlice, baseDenom := denominternal.ExtractPathAndBaseFromFullDenom(denomSplit) + pathSlice, baseDenom := extractPathAndBaseFromFullDenom(denomSplit) return DenomTrace{ Path: strings.Join(pathSlice, "/"), BaseDenom: baseDenom, @@ -81,6 +82,56 @@ func (dt DenomTrace) IsNativeDenom() bool { return dt.Path == "" } +// extractPathAndBaseFromFullDenom returns the trace path and the base denom from +// the elements that constitute the complete denom. +func extractPathAndBaseFromFullDenom(fullDenomItems []string) ([]string, string) { + var ( + pathSlice []string + baseDenomSlice []string + ) + + length := len(fullDenomItems) + for i := 0; i < length; i += 2 { + // The IBC specification does not guarantee the expected format of the + // destination port or destination channel identifier. A short term solution + // to determine base denomination is to expect the channel identifier to be the + // one ibc-go specifies. A longer term solution is to separate the path and base + // denomination in the ICS20 packet. If an intermediate hop prefixes the full denom + // with a channel identifier format different from our own, the base denomination + // will be incorrectly parsed, but the token will continue to be treated correctly + // as an IBC denomination. The hash used to store the token internally on our chain + // will be the same value as the base denomination being correctly parsed. + if i < length-1 && length > 2 && channeltypes.IsValidChannelID(fullDenomItems[i+1]) { + pathSlice = append(pathSlice, fullDenomItems[i], fullDenomItems[i+1]) + } else { + baseDenomSlice = fullDenomItems[i:] + break + } + } + + baseDenom := strings.Join(baseDenomSlice, "/") + + return pathSlice, baseDenom +} + +// validateTraceIdentifiers validates the correctness of the trace associated with a particular base denom. +func validateTraceIdentifiers(identifiers []string) error { + if len(identifiers) == 0 || len(identifiers)%2 != 0 { + return fmt.Errorf("trace info must come in pairs of port and channel identifiers '{portID}/{channelID}', got the identifiers: %s", identifiers) + } + + // validate correctness of port and channel identifiers + for i := 0; i < len(identifiers); i += 2 { + if err := host.PortIdentifierValidator(identifiers[i]); err != nil { + return errorsmod.Wrapf(err, "invalid port ID at position %d", i) + } + if err := host.ChannelIdentifierValidator(identifiers[i+1]); err != nil { + return errorsmod.Wrapf(err, "invalid channel ID at position %d", i) + } + } + return nil +} + // Validate performs a basic validation of the DenomTrace fields. func (dt DenomTrace) Validate() error { // empty trace is accepted when token lives on the original chain @@ -94,7 +145,7 @@ func (dt DenomTrace) Validate() error { // NOTE: no base denomination validation identifiers := strings.Split(dt.Path, "/") - return denominternal.ValidateTraceIdentifiers(identifiers) + return validateTraceIdentifiers(identifiers) } // Traces defines a wrapper type for a slice of DenomTrace. @@ -148,7 +199,7 @@ func ValidatePrefixedDenom(denom string) error { return nil } - pathSlice, baseDenom := denominternal.ExtractPathAndBaseFromFullDenom(denomSplit) + pathSlice, baseDenom := extractPathAndBaseFromFullDenom(denomSplit) if strings.TrimSpace(baseDenom) == "" { return errorsmod.Wrap(ErrInvalidDenomForTransfer, "base denomination cannot be blank") } @@ -160,7 +211,7 @@ func ValidatePrefixedDenom(denom string) error { } identifiers := strings.Split(path, "/") - return denominternal.ValidateTraceIdentifiers(identifiers) + return validateTraceIdentifiers(identifiers) } // validateIBCDenom validates that the given denomination is either: From 50ccd94580757a81dd6742ad8377b2c8377f7282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Thu, 23 May 2024 17:42:15 +0200 Subject: [PATCH 29/42] imp: ics20 v2 self review part 3 (#6373) * imp: self review items * apply jim's suggestion * Update modules/apps/transfer/keeper/msg_server_test.go * Update modules/apps/transfer/ibc_module.go * Update modules/apps/transfer/ibc_module.go --- modules/apps/transfer/ibc_module.go | 31 ++++++++++--------- modules/apps/transfer/keeper/msg_server.go | 6 ++-- .../apps/transfer/keeper/msg_server_test.go | 30 ++++++++++++++++++ modules/apps/transfer/types/events.go | 3 +- .../apps/transfer/types/expected_keepers.go | 2 +- modules/apps/transfer/types/msgs.go | 2 +- modules/apps/transfer/types/token.go | 14 +++++---- modules/apps/transfer/types/token_test.go | 27 ++++++++++++++++ 8 files changed, 86 insertions(+), 29 deletions(-) diff --git a/modules/apps/transfer/ibc_module.go b/modules/apps/transfer/ibc_module.go index c87591e6c9f..f8435946c47 100644 --- a/modules/apps/transfer/ibc_module.go +++ b/modules/apps/transfer/ibc_module.go @@ -181,7 +181,7 @@ func (IBCModule) unmarshalPacketDataBytesToICS20V2(bz []byte, ics20Version strin case types.V1: var datav1 types.FungibleTokenPacketData if err := json.Unmarshal(bz, &datav1); err != nil { - return types.FungibleTokenPacketDataV2{}, errorsmod.Wrapf(ibcerrors.ErrInvalidType, "cannot unmarshal ICS20-V2 transfer packet data: %s", err.Error()) + return types.FungibleTokenPacketDataV2{}, errorsmod.Wrapf(ibcerrors.ErrInvalidType, "cannot unmarshal ICS20-V1 transfer packet data: %s", err.Error()) } if err := datav1.ValidateBasic(); err != nil { @@ -240,26 +240,28 @@ func (im IBCModule) OnRecvPacket( } } - ctx.EventManager().EmitEvent(sdk.NewEvent( - types.EventTypePacket, + eventAttributes := []sdk.Attribute{ sdk.NewAttribute(types.AttributeKeySender, data.Sender), sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), sdk.NewAttribute(types.AttributeKeyTokens, types.Tokens(data.Tokens).String()), sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), - )) - - eventAttributes := []sdk.Attribute{ - sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), sdk.NewAttribute(types.AttributeKeyAckSuccess, fmt.Sprintf("%t", ack.Success())), } + if ackErr != nil { eventAttributes = append(eventAttributes, sdk.NewAttribute(types.AttributeKeyAckError, ackErr.Error())) } - ctx.EventManager().EmitEvent(sdk.NewEvent( - sdk.EventTypeMessage, - eventAttributes..., - )) + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypePacket, + eventAttributes..., + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + ), + }) // NOTE: acknowledgement will be written synchronously during IBC handler execution. return ack @@ -298,11 +300,11 @@ func (im IBCModule) OnAcknowledgementPacket( sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), sdk.NewAttribute(types.AttributeKeyTokens, types.Tokens(data.Tokens).String()), sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), + sdk.NewAttribute(types.AttributeKeyAck, ack.String()), ), sdk.NewEvent( sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), - sdk.NewAttribute(types.AttributeKeyAck, ack.String()), ), }) @@ -350,9 +352,8 @@ func (im IBCModule) OnTimeoutPacket( ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeTimeout, - sdk.NewAttribute(sdk.AttributeKeySender, data.Sender), - sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), - sdk.NewAttribute(types.AttributeKeyTokens, types.Tokens(data.Tokens).String()), + sdk.NewAttribute(types.AttributeKeyReceiver, data.Sender), + sdk.NewAttribute(types.AttributeKeyRefundTokens, types.Tokens(data.Tokens).String()), sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), ), sdk.NewEvent( diff --git a/modules/apps/transfer/keeper/msg_server.go b/modules/apps/transfer/keeper/msg_server.go index eaac2fb3ae9..f6a04ab4b81 100644 --- a/modules/apps/transfer/keeper/msg_server.go +++ b/modules/apps/transfer/keeper/msg_server.go @@ -38,10 +38,8 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types. return nil, errorsmod.Wrapf(ibcerrors.ErrInvalidRequest, "cannot transfer multiple coins with ics20-1") } - for _, coin := range coins { - if !k.bankKeeper.IsSendEnabledCoin(ctx, coin) { - return nil, errorsmod.Wrapf(types.ErrSendDisabled, "transfers are currently disabled for %s", coin.Denom) - } + if err := k.bankKeeper.IsSendEnabledCoins(ctx, coins...); err != nil { + return nil, errorsmod.Wrapf(types.ErrSendDisabled, err.Error()) } if k.bankKeeper.BlockedAddr(sender) { diff --git a/modules/apps/transfer/keeper/msg_server_test.go b/modules/apps/transfer/keeper/msg_server_test.go index e6d04001c4f..4806fc23f5d 100644 --- a/modules/apps/transfer/keeper/msg_server_test.go +++ b/modules/apps/transfer/keeper/msg_server_test.go @@ -113,6 +113,36 @@ func (suite *KeeperTestSuite) TestMsgTransfer() { types.ErrSendDisabled, false, }, + { + "failure: bank send disabled for coin in multi coin transfer", + func() { + coin2 = sdk.NewCoin("bond", sdkmath.NewInt(100)) + coins := sdk.NewCoins(coin1, coin2) + + // send some coins of the second denom from bank module to the sender account as well + suite.Require().NoError(suite.chainA.GetSimApp().BankKeeper.MintCoins(suite.chainA.GetContext(), types.ModuleName, sdk.NewCoins(coin2))) + suite.Require().NoError(suite.chainA.GetSimApp().BankKeeper.SendCoinsFromModuleToAccount(suite.chainA.GetContext(), types.ModuleName, suite.chainA.SenderAccount.GetAddress(), sdk.NewCoins(coin2))) + + msg = types.NewMsgTransfer( + path.EndpointA.ChannelConfig.PortID, + path.EndpointA.ChannelID, + coins, + suite.chainA.SenderAccount.GetAddress().String(), + suite.chainB.SenderAccount.GetAddress().String(), + suite.chainB.GetTimeoutHeight(), 0, // only use timeout height + "memo", + ) + + err := suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), + banktypes.Params{ + SendEnabled: []*banktypes.SendEnabled{{Denom: coin2.Denom, Enabled: false}}, + }, + ) + suite.Require().NoError(err) + }, + types.ErrSendDisabled, + true, + }, { "failure: channel does not exist", func() { diff --git a/modules/apps/transfer/types/events.go b/modules/apps/transfer/types/events.go index 7a4bfaf2a04..f310ba5d0d9 100644 --- a/modules/apps/transfer/types/events.go +++ b/modules/apps/transfer/types/events.go @@ -13,8 +13,7 @@ const ( AttributeKeyDenom = "denom" AttributeKeyTokens = "tokens" AttributeKeyRefundReceiver = "refund_receiver" - AttributeKeyRefundDenom = "refund_denom" - AttributeKeyRefundAmount = "refund_amount" + AttributeKeyRefundTokens = "refund_tokens" AttributeKeyAckSuccess = "success" AttributeKeyAck = "acknowledgement" AttributeKeyAckError = "error" diff --git a/modules/apps/transfer/types/expected_keepers.go b/modules/apps/transfer/types/expected_keepers.go index e639a84606f..9ed02474405 100644 --- a/modules/apps/transfer/types/expected_keepers.go +++ b/modules/apps/transfer/types/expected_keepers.go @@ -27,7 +27,7 @@ type BankKeeper interface { SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error BlockedAddr(addr sdk.AccAddress) bool - IsSendEnabledCoin(ctx context.Context, coin sdk.Coin) bool + IsSendEnabledCoins(ctx context.Context, coins ...sdk.Coin) error HasDenomMetaData(ctx context.Context, denom string) bool SetDenomMetaData(ctx context.Context, denomMetaData banktypes.Metadata) GetBalance(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin diff --git a/modules/apps/transfer/types/msgs.go b/modules/apps/transfer/types/msgs.go index fd28b8778e5..3fdfcfefe85 100644 --- a/modules/apps/transfer/types/msgs.go +++ b/modules/apps/transfer/types/msgs.go @@ -126,7 +126,7 @@ func isValidIBCCoin(coin sdk.Coin) bool { return validateIBCCoin(coin) == nil } -// isValidIBCCoin returns true if the token provided is valid, +// validateIBCCoin returns true if the token provided is valid, // and should be used to transfer tokens. The token must // have a positive amount. func validateIBCCoin(coin sdk.Coin) error { diff --git a/modules/apps/transfer/types/token.go b/modules/apps/transfer/types/token.go index 4fe2928489d..68fd3383b75 100644 --- a/modules/apps/transfer/types/token.go +++ b/modules/apps/transfer/types/token.go @@ -22,14 +22,16 @@ func (t Token) Validate() error { return errorsmod.Wrapf(ErrInvalidAmount, "amount must be strictly positive: got %d", amount) } - if len(t.Trace) == 0 { - return nil - } + if len(t.Trace) != 0 { + trace := strings.Join(t.Trace, "/") + identifiers := strings.Split(trace, "/") - trace := strings.Join(t.Trace, "/") - identifiers := strings.Split(trace, "/") + if err := validateTraceIdentifiers(identifiers); err != nil { + return err + } + } - return validateTraceIdentifiers(identifiers) + return nil } // GetFullDenomPath returns the full denomination according to the ICS20 specification: diff --git a/modules/apps/transfer/types/token_test.go b/modules/apps/transfer/types/token_test.go index 7ae88757b90..c80fb975ec9 100644 --- a/modules/apps/transfer/types/token_test.go +++ b/modules/apps/transfer/types/token_test.go @@ -110,6 +110,33 @@ func TestValidate(t *testing.T) { }, ErrInvalidDenomForTransfer, }, + { + "failure: invalid amount string", + Token{ + Denom: "atom", + Amount: "value", + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + ErrInvalidAmount, + }, + { + "failure: amount is zero", + Token{ + Denom: "atom", + Amount: "0", + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + ErrInvalidAmount, + }, + { + "failure: amount is negative", + Token{ + Denom: "atom", + Amount: "-1", + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + ErrInvalidAmount, + }, { "failure: invalid identifier in trace", Token{ From 87eb32e5cd0918b2ddd0f5ff93dd6e9a8c1b1207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Colin=20Axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Thu, 23 May 2024 17:45:19 +0200 Subject: [PATCH 30/42] chore: remove duplicate test case --- modules/apps/transfer/ibc_module_test.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/modules/apps/transfer/ibc_module_test.go b/modules/apps/transfer/ibc_module_test.go index e52b04fc7da..71f261480ed 100644 --- a/modules/apps/transfer/ibc_module_test.go +++ b/modules/apps/transfer/ibc_module_test.go @@ -144,12 +144,7 @@ func (suite *TransferTestSuite) TestOnChanOpenTry() { }, nil, types.V1, }, { - "success: invalid counterparty version, we use our proposed version", func() { - counterpartyVersion = "version" - }, nil, types.V2, - }, - { - "success: invalid counterparty version proposes new version", func() { + "success: invalid counterparty version, we propose new version", func() { // transfer module will propose the default version counterpartyVersion = "version" }, nil, types.V2, From e8b9d5a918a43310201b4b24db8052127a1884d2 Mon Sep 17 00:00:00 2001 From: DimitrisJim Date: Thu, 23 May 2024 19:39:11 +0300 Subject: [PATCH 31/42] chore: address minor nits (#6374) --- modules/apps/transfer/types/msgs_test.go | 2 +- modules/apps/transfer/types/packet.go | 2 +- modules/apps/transfer/types/packet_test.go | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/modules/apps/transfer/types/msgs_test.go b/modules/apps/transfer/types/msgs_test.go index 602551980c6..f1361214d02 100644 --- a/modules/apps/transfer/types/msgs_test.go +++ b/modules/apps/transfer/types/msgs_test.go @@ -42,7 +42,7 @@ var ( ibcCoins = sdk.NewCoins(sdk.NewCoin("ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdkmath.NewInt(100))) invalidIBCCoins = sdk.NewCoins(sdk.NewCoin("ibc/7F1D3FCF4AE79E1554", sdkmath.NewInt(100))) invalidDenomCoins = []sdk.Coin{{Denom: "0atom", Amount: sdkmath.NewInt(100)}} - zeroCoins = sdk.NewCoins(sdk.Coin{Denom: "atoms", Amount: sdkmath.NewInt(0)}) + zeroCoins = []sdk.Coin{{Denom: "atoms", Amount: sdkmath.NewInt(0)}} timeoutHeight = clienttypes.NewHeight(0, 10) ) diff --git a/modules/apps/transfer/types/packet.go b/modules/apps/transfer/types/packet.go index 649bc7cddad..e706084d56a 100644 --- a/modules/apps/transfer/types/packet.go +++ b/modules/apps/transfer/types/packet.go @@ -97,7 +97,7 @@ func (ftpd FungibleTokenPacketData) GetCustomPacketData(key string) interface{} return memoData } -// NewFungibleTokenPacketDataV2 constructs a new NewFungibleTokenPacketDataV2 instance +// NewFungibleTokenPacketDataV2 constructs a new FungibleTokenPacketDataV2 instance func NewFungibleTokenPacketDataV2( tokens []Token, sender, receiver string, diff --git a/modules/apps/transfer/types/packet_test.go b/modules/apps/transfer/types/packet_test.go index 9292f207f7f..71a0cfc81c7 100644 --- a/modules/apps/transfer/types/packet_test.go +++ b/modules/apps/transfer/types/packet_test.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" + ibctesting "github.com/cosmos/ibc-go/v8/testing" ) const ( @@ -338,6 +339,22 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { ), ibcerrors.ErrInvalidAddress, }, + { + "failure: memo field too large", + types.NewFungibleTokenPacketDataV2( + []types.Token{ + { + Denom: denom, + Amount: largeAmount, + Trace: []string{"transfer/channel-0", "transfer/channel-1"}, + }, + }, + sender, + receiver, + ibctesting.GenerateString(types.MaximumMemoLength+1), + ), + types.ErrInvalidMemo, + }, } for _, tc := range testCases { From 6d40bc67ccd669456651bd7d584762f49006a157 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Fri, 24 May 2024 09:25:52 +0100 Subject: [PATCH 32/42] Refactor msgs_test.go to use expError (#6367) * chore: refactoring msgs_test.go to use expError * chore: updating expected errors * chore: update MsgUpdateParams and lint --------- Co-authored-by: DimitrisJim --- modules/apps/transfer/types/msgs.go | 4 +- modules/apps/transfer/types/msgs_test.go | 83 +++++++++++++----------- 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/modules/apps/transfer/types/msgs.go b/modules/apps/transfer/types/msgs.go index 3fdfcfefe85..c5fd206d2c4 100644 --- a/modules/apps/transfer/types/msgs.go +++ b/modules/apps/transfer/types/msgs.go @@ -102,7 +102,7 @@ func (msg MsgTransfer) ValidateBasic() error { for _, coin := range msg.GetCoins() { if err := validateIBCCoin(coin); err != nil { - return errorsmod.Wrap(err, coin.String()) + return errorsmod.Wrapf(ibcerrors.ErrInvalidCoins, "%s: %s", err.Error(), coin.String()) } } @@ -137,7 +137,7 @@ func validateIBCCoin(coin sdk.Coin) error { return errorsmod.Wrap(ErrInvalidAmount, "amount must be positive") } if err := validateIBCDenom(coin.GetDenom()); err != nil { - return err + return errorsmod.Wrap(ErrInvalidDenomForTransfer, err.Error()) } return nil diff --git a/modules/apps/transfer/types/msgs_test.go b/modules/apps/transfer/types/msgs_test.go index f1361214d02..f05ad3cdc68 100644 --- a/modules/apps/transfer/types/msgs_test.go +++ b/modules/apps/transfer/types/msgs_test.go @@ -14,6 +14,8 @@ import ( "github.com/cosmos/ibc-go/v8/modules/apps/transfer" "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v8/modules/core/24-host" + ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" ibctesting "github.com/cosmos/ibc-go/v8/testing" ) @@ -50,41 +52,43 @@ var ( // TestMsgTransferValidation tests ValidateBasic for MsgTransfer func TestMsgTransferValidation(t *testing.T) { testCases := []struct { - name string - msg *types.MsgTransfer - expPass bool + name string + msg *types.MsgTransfer + expError error }{ - {"valid msg with base denom", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ""), true}, - {"valid msg with trace hash", types.NewMsgTransfer(validPort, validChannel, ibcCoins, sender, receiver, timeoutHeight, 0, ""), true}, - {"multidenom", types.NewMsgTransfer(validPort, validChannel, coins.Add(ibcCoins...), sender, receiver, timeoutHeight, 0, ""), true}, - {"invalid ibc denom", types.NewMsgTransfer(validPort, validChannel, invalidIBCCoins, sender, receiver, timeoutHeight, 0, ""), false}, - {"too short port id", types.NewMsgTransfer(invalidShortPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, - {"too long port id", types.NewMsgTransfer(invalidLongPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, - {"port id contains non-alpha", types.NewMsgTransfer(invalidPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, - {"too short channel id", types.NewMsgTransfer(validPort, invalidShortChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, - {"too long channel id", types.NewMsgTransfer(validPort, invalidLongChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, - {"too long memo", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ibctesting.GenerateString(types.MaximumMemoLength+1)), false}, - {"channel id contains non-alpha", types.NewMsgTransfer(validPort, invalidChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, - {"invalid denom", types.NewMsgTransfer(validPort, validChannel, invalidDenomCoins, sender, receiver, timeoutHeight, 0, ""), false}, - {"zero coins", types.NewMsgTransfer(validPort, validChannel, zeroCoins, sender, receiver, timeoutHeight, 0, ""), false}, - {"missing sender address", types.NewMsgTransfer(validPort, validChannel, coins, emptyAddr, receiver, timeoutHeight, 0, ""), false}, - {"missing recipient address", types.NewMsgTransfer(validPort, validChannel, coins, sender, "", timeoutHeight, 0, ""), false}, - {"too long recipient address", types.NewMsgTransfer(validPort, validChannel, coins, sender, ibctesting.GenerateString(types.MaximumReceiverLength+1), timeoutHeight, 0, ""), false}, - {"empty coins", types.NewMsgTransfer(validPort, validChannel, sdk.NewCoins(), sender, receiver, timeoutHeight, 0, ""), false}, - {"multidenom: invalid denom", types.NewMsgTransfer(validPort, validChannel, coins.Add(invalidDenomCoins...), sender, receiver, timeoutHeight, 0, ""), false}, - {"multidenom: invalid ibc denom", types.NewMsgTransfer(validPort, validChannel, coins.Add(invalidIBCCoins...), sender, receiver, timeoutHeight, 0, ""), false}, - {"multidenom: zero coins", types.NewMsgTransfer(validPort, validChannel, zeroCoins, sender, receiver, timeoutHeight, 0, ""), false}, - {"multidenom: too many coins", types.NewMsgTransfer(validPort, validChannel, make([]sdk.Coin, types.MaximumTokensLength+1), sender, receiver, timeoutHeight, 0, ""), false}, + {"valid msg with base denom", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ""), nil}, + {"valid msg with trace hash", types.NewMsgTransfer(validPort, validChannel, ibcCoins, sender, receiver, timeoutHeight, 0, ""), nil}, + {"multidenom", types.NewMsgTransfer(validPort, validChannel, coins.Add(ibcCoins...), sender, receiver, timeoutHeight, 0, ""), nil}, + {"invalid ibc denom", types.NewMsgTransfer(validPort, validChannel, invalidIBCCoins, sender, receiver, timeoutHeight, 0, ""), ibcerrors.ErrInvalidCoins}, + {"too short port id", types.NewMsgTransfer(invalidShortPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ""), host.ErrInvalidID}, + {"too long port id", types.NewMsgTransfer(invalidLongPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ""), host.ErrInvalidID}, + {"port id contains non-alpha", types.NewMsgTransfer(invalidPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ""), host.ErrInvalidID}, + {"too short channel id", types.NewMsgTransfer(validPort, invalidShortChannel, coins, sender, receiver, timeoutHeight, 0, ""), host.ErrInvalidID}, + {"too long channel id", types.NewMsgTransfer(validPort, invalidLongChannel, coins, sender, receiver, timeoutHeight, 0, ""), host.ErrInvalidID}, + {"too long memo", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ibctesting.GenerateString(types.MaximumMemoLength+1)), types.ErrInvalidMemo}, + {"channel id contains non-alpha", types.NewMsgTransfer(validPort, invalidChannel, coins, sender, receiver, timeoutHeight, 0, ""), host.ErrInvalidID}, + {"invalid denom", types.NewMsgTransfer(validPort, validChannel, invalidDenomCoins, sender, receiver, timeoutHeight, 0, ""), ibcerrors.ErrInvalidCoins}, + {"zero coins", types.NewMsgTransfer(validPort, validChannel, zeroCoins, sender, receiver, timeoutHeight, 0, ""), ibcerrors.ErrInvalidCoins}, + {"missing sender address", types.NewMsgTransfer(validPort, validChannel, coins, emptyAddr, receiver, timeoutHeight, 0, ""), ibcerrors.ErrInvalidAddress}, + {"missing recipient address", types.NewMsgTransfer(validPort, validChannel, coins, sender, "", timeoutHeight, 0, ""), ibcerrors.ErrInvalidAddress}, + {"too long recipient address", types.NewMsgTransfer(validPort, validChannel, coins, sender, ibctesting.GenerateString(types.MaximumReceiverLength+1), timeoutHeight, 0, ""), ibcerrors.ErrInvalidAddress}, + {"empty coins", types.NewMsgTransfer(validPort, validChannel, sdk.NewCoins(), sender, receiver, timeoutHeight, 0, ""), ibcerrors.ErrInvalidCoins}, + {"multidenom: invalid denom", types.NewMsgTransfer(validPort, validChannel, coins.Add(invalidDenomCoins...), sender, receiver, timeoutHeight, 0, ""), ibcerrors.ErrInvalidCoins}, + {"multidenom: invalid ibc denom", types.NewMsgTransfer(validPort, validChannel, coins.Add(invalidIBCCoins...), sender, receiver, timeoutHeight, 0, ""), ibcerrors.ErrInvalidCoins}, + {"multidenom: zero coins", types.NewMsgTransfer(validPort, validChannel, zeroCoins, sender, receiver, timeoutHeight, 0, ""), ibcerrors.ErrInvalidCoins}, + {"multidenom: too many coins", types.NewMsgTransfer(validPort, validChannel, make([]sdk.Coin, types.MaximumTokensLength+1), sender, receiver, timeoutHeight, 0, ""), ibcerrors.ErrInvalidCoins}, } - for i, tc := range testCases { + for _, tc := range testCases { tc := tc err := tc.msg.ValidateBasic() - if tc.expPass { - require.NoError(t, err, "valid test case %d failed: %s", i, tc.name) + + expPass := tc.expError == nil + if expPass { + require.NoError(t, err) } else { - require.Error(t, err, "invalid test case %d passed: %s", i, tc.name) + require.ErrorIs(t, err, tc.expError) } } } @@ -103,23 +107,25 @@ func TestMsgTransferGetSigners(t *testing.T) { // TestMsgUpdateParamsValidateBasic tests ValidateBasic for MsgUpdateParams func TestMsgUpdateParamsValidateBasic(t *testing.T) { testCases := []struct { - name string - msg *types.MsgUpdateParams - expPass bool + name string + msg *types.MsgUpdateParams + expError error }{ - {"success: valid signer and valid params", types.NewMsgUpdateParams(ibctesting.TestAccAddress, types.DefaultParams()), true}, - {"failure: invalid signer with valid params", types.NewMsgUpdateParams(invalidAddress, types.DefaultParams()), false}, - {"failure: empty signer with valid params", types.NewMsgUpdateParams(emptyAddr, types.DefaultParams()), false}, + {"success: valid signer and valid params", types.NewMsgUpdateParams(ibctesting.TestAccAddress, types.DefaultParams()), nil}, + {"failure: invalid signer with valid params", types.NewMsgUpdateParams(invalidAddress, types.DefaultParams()), ibcerrors.ErrInvalidAddress}, + {"failure: empty signer with valid params", types.NewMsgUpdateParams(emptyAddr, types.DefaultParams()), ibcerrors.ErrInvalidAddress}, } - for i, tc := range testCases { + for _, tc := range testCases { tc := tc err := tc.msg.ValidateBasic() - if tc.expPass { - require.NoError(t, err, "valid test case %d failed: %s", i, tc.name) + + expPass := tc.expError == nil + if expPass { + require.NoError(t, err) } else { - require.Error(t, err, "invalid test case %d passed: %s", i, tc.name) + require.ErrorIs(t, err, tc.expError) } } } @@ -145,6 +151,7 @@ func TestMsgUpdateParamsGetSigners(t *testing.T) { encodingCfg := moduletestutil.MakeTestEncodingConfig(transfer.AppModuleBasic{}) signers, _, err := encodingCfg.Codec.GetMsgV1Signers(&msg) + if tc.expPass { require.NoError(t, err) require.Equal(t, tc.address.Bytes(), signers[0]) From c171059ca126faf7cdb5b3e3d473783296762290 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Fri, 24 May 2024 12:31:21 +0100 Subject: [PATCH 33/42] chore: remove unused chain variable in setup (#6371) --- modules/apps/callbacks/types/types_test.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/modules/apps/callbacks/types/types_test.go b/modules/apps/callbacks/types/types_test.go index 5843f972249..5aab45242d2 100644 --- a/modules/apps/callbacks/types/types_test.go +++ b/modules/apps/callbacks/types/types_test.go @@ -14,19 +14,16 @@ type CallbacksTypesTestSuite struct { coord *ibctesting.Coordinator - chain *ibctesting.TestChain - chainA, chainB *ibctesting.TestChain path *ibctesting.Path } -// SetupTest creates a coordinator with 1 test chain. +// SetupTest creates a coordinator with 2 test chains. func (s *CallbacksTypesTestSuite) SetupTest() { - s.coord = ibctesting.NewCoordinator(s.T(), 3) - s.chain = s.coord.GetChain(ibctesting.GetChainID(1)) - s.chainA = s.coord.GetChain(ibctesting.GetChainID(2)) - s.chainB = s.coord.GetChain(ibctesting.GetChainID(3)) + s.coord = ibctesting.NewCoordinator(s.T(), 2) + s.chainA = s.coord.GetChain(ibctesting.GetChainID(1)) + s.chainB = s.coord.GetChain(ibctesting.GetChainID(2)) s.path = ibctesting.NewPath(s.chainA, s.chainB) } From d55c817bf71dd6bbc97823b8cae3b3b9f5ed8d44 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 27 May 2024 10:26:45 +0200 Subject: [PATCH 34/42] use new queries in e2e --- e2e/tests/transfer/authz_test.go | 4 ++-- e2e/tests/transfer/base_test.go | 8 ++++---- e2e/tests/transfer/upgrades_test.go | 26 +++++++++++++------------- e2e/testsuite/testsuite.go | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/e2e/tests/transfer/authz_test.go b/e2e/tests/transfer/authz_test.go index 08bb9ce3f2e..0e1f0a121aa 100644 --- a/e2e/tests/transfer/authz_test.go +++ b/e2e/tests/transfer/authz_test.go @@ -48,7 +48,7 @@ func (suite *AuthzTransferTestSuite) TestAuthz_MsgTransfer_Succeeds() { t := suite.T() ctx := context.TODO() - relayer, channelA := suite.SetupChainsRelayerAndChannel(ctx, suite.TransferChannelOptions(transfertypes.Version)) + relayer, channelA := suite.SetupChainsRelayerAndChannel(ctx, suite.TransferChannelOptions(transfertypes.V1)) chainA, chainB := suite.GetChains() chainADenom := chainA.Config().Denom @@ -205,7 +205,7 @@ func (suite *AuthzTransferTestSuite) TestAuthz_InvalidTransferAuthorizations() { t := suite.T() ctx := context.TODO() - relayer, channelA := suite.SetupChainsRelayerAndChannel(ctx, suite.TransferChannelOptions(transfertypes.Version)) + relayer, channelA := suite.SetupChainsRelayerAndChannel(ctx, suite.TransferChannelOptions(transfertypes.V1)) chainA, chainB := suite.GetChains() chainAVersion := chainA.Config().Images[0].Version diff --git a/e2e/tests/transfer/base_test.go b/e2e/tests/transfer/base_test.go index a58c33a377f..d0f54a9ad30 100644 --- a/e2e/tests/transfer/base_test.go +++ b/e2e/tests/transfer/base_test.go @@ -186,7 +186,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom( expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount s.Require().Equal(expected, actualBalance) - actualTotalEscrow, err := s.QueryTotalEscrowForDenom(ctx, chainA, chainADenom) + actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) s.Require().NoError(err) expectedTotalEscrow := sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.IBCTransferAmount)) @@ -200,7 +200,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom( t.Run("packets are relayed", func(t *testing.T) { s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) - actualBalance, err := s.QueryBalance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) s.Require().NoError(err) expected := testvalues.IBCTransferAmount @@ -237,7 +237,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom( }) t.Run("chain B ibc denom", func(t *testing.T) { - actualBalance, err := s.QueryBalance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) + actualBalance, err := query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) s.Require().NoError(err) expected := testvalues.IBCTransferAmount @@ -246,7 +246,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom( }) t.Run("tokens are un-escrowed", func(t *testing.T) { - actualTotalEscrow, err := s.QueryTotalEscrowForDenom(ctx, chainA, chainADenom) + actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) s.Require().NoError(err) s.Require().Equal(sdk.NewCoin(chainADenom, sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because tokens have come back }) diff --git a/e2e/tests/transfer/upgrades_test.go b/e2e/tests/transfer/upgrades_test.go index 11922e671eb..5190c034d9b 100644 --- a/e2e/tests/transfer/upgrades_test.go +++ b/e2e/tests/transfer/upgrades_test.go @@ -242,7 +242,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succee t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.Version1)) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions(transfertypes.V1)) channelB := channelA.Counterparty chainA, chainB := s.GetChains() @@ -260,9 +260,9 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succee s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") t.Run("verify transfer version of channel A is ics20-1", func(t *testing.T) { - channel, err := s.QueryChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + channel, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) s.Require().NoError(err) - s.Require().Equal(transfertypes.Version1, channel.Version, "the channel version is not ics20-1") + s.Require().Equal(transfertypes.V1, channel.Version, "the channel version is not ics20-1") }) t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { @@ -286,7 +286,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succee expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount s.Require().Equal(expected, actualBalance) - actualTotalEscrow, err := s.QueryTotalEscrowForDenom(ctx, chainA, chainADenom) + actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) s.Require().NoError(err) expectedTotalEscrow := sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.IBCTransferAmount)) @@ -300,7 +300,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succee t.Run("packets are relayed", func(t *testing.T) { s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) - actualBalance, err := s.QueryBalance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) s.Require().NoError(err) expected := testvalues.IBCTransferAmount @@ -308,25 +308,25 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succee }) t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) { - chA, err := s.QueryChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + chA, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) s.Require().NoError(err) - upgradeFields := channeltypes.NewUpgradeFields(chA.Ordering, chA.ConnectionHops, transfertypes.Version) + upgradeFields := channeltypes.NewUpgradeFields(chA.Ordering, chA.ConnectionHops, transfertypes.V2) s.InitiateChannelUpgrade(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, upgradeFields) }) s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") t.Run("verify channel A upgraded and transfer version is ics20-2", func(t *testing.T) { - channel, err := s.QueryChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + channel, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) s.Require().NoError(err) - s.Require().Equal(transfertypes.Version, channel.Version, "the channel version is not ics20-2") + s.Require().Equal(transfertypes.V2, channel.Version, "the channel version is not ics20-2") }) t.Run("verify channel B upgraded and transfer version is ics20-2", func(t *testing.T) { - channel, err := s.QueryChannel(ctx, chainB, channelB.PortID, channelB.ChannelID) + channel, err := query.Channel(ctx, chainB, channelB.PortID, channelB.ChannelID) s.Require().NoError(err) - s.Require().Equal(transfertypes.Version, channel.Version, "the channel version is not ics20-2") + s.Require().Equal(transfertypes.V2, channel.Version, "the channel version is not ics20-2") }) // send the native chainB denom and also the ibc token from chainA @@ -355,7 +355,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succee }) t.Run("chain B ibc denom", func(t *testing.T) { - actualBalance, err := s.QueryBalance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) + actualBalance, err := query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) s.Require().NoError(err) expected := testvalues.IBCTransferAmount @@ -364,7 +364,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succee }) t.Run("tokens are un-escrowed", func(t *testing.T) { - actualTotalEscrow, err := s.QueryTotalEscrowForDenom(ctx, chainA, chainADenom) + actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) s.Require().NoError(err) s.Require().Equal(sdk.NewCoin(chainADenom, sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because tokens have come back }) diff --git a/e2e/testsuite/testsuite.go b/e2e/testsuite/testsuite.go index 4f029d7616e..5938ec9ce97 100644 --- a/e2e/testsuite/testsuite.go +++ b/e2e/testsuite/testsuite.go @@ -453,7 +453,7 @@ func (s *E2ETestSuite) GetRelayerExecReporter() *testreporter.RelayerExecReporte // TransferChannelOptions configures both of the chains to have non-incentivized transfer channels. func (*E2ETestSuite) TransferChannelOptions(version string) func(options *ibc.CreateChannelOptions) { return func(opts *ibc.CreateChannelOptions) { - opts.Version = transfertypes.V2 + opts.Version = version opts.SourcePortName = transfertypes.PortID opts.DestPortName = transfertypes.PortID } From 3319e42ab1d712f061f40780c3c38d2d44acc071 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 27 May 2024 21:47:55 +0200 Subject: [PATCH 35/42] add test for error ack multidenom transfer --- e2e/tests/transfer/base_test.go | 112 +++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 2 deletions(-) diff --git a/e2e/tests/transfer/base_test.go b/e2e/tests/transfer/base_test.go index d0f54a9ad30..e6ef1ccb1b5 100644 --- a/e2e/tests/transfer/base_test.go +++ b/e2e/tests/transfer/base_test.go @@ -179,7 +179,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom( s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") - t.Run("tokens are escrowed", func(t *testing.T) { + t.Run("native chainA tokens are escrowed", func(t *testing.T) { actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) s.Require().NoError(err) @@ -245,13 +245,121 @@ func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom( }) }) - t.Run("tokens are un-escrowed", func(t *testing.T) { + t.Run("native chainA tokens are un-escrowed", func(t *testing.T) { actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) s.Require().NoError(err) s.Require().Equal(sdk.NewCoin(chainADenom, sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because tokens have come back }) } +// TestMsgTransfer_Fails_InvalidAddress_MultiDenom attempts to send a multidenom IBC transfer +// to an invalid address and ensures that the tokens on the sending chain are returned to the sender. +func (s *TransferTestSuite) TestMsgTransfer_Fails_InvalidAddress_MultiDenom() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, nil) + chainA, chainB := s.GetChains() + + chainADenom := chainA.Config().Denom + chainBDenom := chainB.Config().Denom + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.FormattedAddress() + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) + + t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + s.AssertTxSuccess(transferTxResp) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + t.Run("native chainA 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) + + actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) + s.Require().NoError(err) + + expectedTotalEscrow := sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.IBCTransferAmount)) + s.Require().Equal(expectedTotalEscrow, actualTotalEscrow) + }) + + 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 := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + t.Run("metadata for IBC denomination exists on chainB", func(t *testing.T) { + s.AssertHumanReadableDenom(ctx, chainB, chainADenom, channelA) + }) + + // send the native chainB denom and also the ibc token from chainA + denoms := []string{chainBIBCToken.IBCDenom(), chainBDenom} + var transferCoins []sdk.Coin + for _, denom := range denoms { + transferCoins = append(transferCoins, testvalues.DefaultTransferAmount(denom)) + } + + t.Run("native token from chain B and non-native IBC token from chainA, both to chainA", func(t *testing.T) { + transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, transferCoins, chainBAddress, testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") + s.AssertTxSuccess(transferTxResp) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + t.Run("native chainB tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainBNativeBalance(ctx, chainBWallet) + 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) + }) + + t.Run("token are returned to sender on chainB", func(t *testing.T) { + t.Run("non-native chainA ibc denom", func(t *testing.T) { + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + t.Run("native chainB denom", func(t *testing.T) { + actualBalance, err := s.GetChainBNativeBalance(ctx, chainBWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, actualBalance) + }) + }) +} + // TestMsgTransfer_Fails_InvalidAddress attempts to send an IBC transfer to an invalid address and ensures // that the tokens on the sending chain are unescrowed. func (s *TransferTestSuite) TestMsgTransfer_Fails_InvalidAddress() { From 6886e3bb22230c4322f7188e01a2492af87d3f6f Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Sat, 8 Jun 2024 21:03:48 +0200 Subject: [PATCH 36/42] downgrade test to ics20-1 --- e2e/tests/transfer/upgrades_test.go | 90 +++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/e2e/tests/transfer/upgrades_test.go b/e2e/tests/transfer/upgrades_test.go index e2bf1fac1f1..b3643a0230a 100644 --- a/e2e/tests/transfer/upgrades_test.go +++ b/e2e/tests/transfer/upgrades_test.go @@ -386,6 +386,96 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succee }) } +// TestChannelDowngrade_WithICS20v1_Succeeds tests downgrading a transfer channel from ICS20 v2 to ICS20 v1. +func (s *TransferChannelUpgradesTestSuite) TestChannelDowngrade_WithICS20v1_Succeeds() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions()) + + channelB := channelA.Counterparty + chainA, chainB := s.GetChains() + + chainADenom := chainA.Config().Denom + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelB.PortID, channelB.ChannelID) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("verify transfer version of channel A is ics20-2", func(t *testing.T) { + channel, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Equal(transfertypes.V2, channel.Version, "the channel version is not ics20-2") + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) { + chA, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + + upgradeFields := channeltypes.NewUpgradeFields(chA.Ordering, chA.ConnectionHops, transfertypes.V1) + s.InitiateChannelUpgrade(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, upgradeFields) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") + + t.Run("verify channel A downgraded and transfer version is ics20-1", func(t *testing.T) { + channel, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Equal(transfertypes.V1, channel.Version, "the channel version is not ics20-1") + }) + + t.Run("verify channel B downgraded and transfer version is ics20-1", func(t *testing.T) { + channel, err := query.Channel(ctx, chainB, channelB.PortID, channelB.ChannelID) + s.Require().NoError(err) + s.Require().Equal(transfertypes.V1, channel.Version, "the channel version is not ics20-1") + }) + + t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + chainBWalletAmount := ibc.WalletAmount{ + Address: chainBWallet.FormattedAddress(), // destination address + Denom: chainA.Config().Denom, + Amount: sdkmath.NewInt(testvalues.IBCTransferAmount), + } + + transferTxResp, err := chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainBWalletAmount, ibc.TransferOptions{}) + s.Require().NoError(err) + s.Require().NoError(transferTxResp.Validate(), "chain-a ibc transfer tx is invalid") + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + 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) + + actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) + s.Require().NoError(err) + + expectedTotalEscrow := sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.IBCTransferAmount)) + s.Require().Equal(expectedTotalEscrow, actualTotalEscrow) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) +} + // TestChannelUpgrade_WithFeeMiddleware_CrossingHello_Succeeds tests upgrading a transfer channel to wire up fee middleware under crossing hello func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_CrossingHello_Succeeds() { t := s.T() From 14d17ffe0b6825d654525d527bcfdc2bad4b67af Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Sun, 9 Jun 2024 15:23:16 +0200 Subject: [PATCH 37/42] add test to upgrade channel to fee middleware and ICS20 v2 --- .../main/transfer-v2-chain-a.json | 17 ++ .../transfer-v2-channel-upgrade-chain-a.json | 19 ++ .../transfer-v2-channel-upgrade.json | 19 ++ .../unreleased/transfer-v2.json | 17 ++ .../core/03-connection/connection_test.go | 1 - e2e/tests/transfer/base_test.go | 2 +- e2e/tests/transfer/upgrades_test.go | 202 +++++++++++++++++- 7 files changed, 266 insertions(+), 11 deletions(-) create mode 100644 .github/compatibility-test-matrices/main/transfer-v2-chain-a.json create mode 100644 .github/compatibility-test-matrices/main/transfer-v2-channel-upgrade-chain-a.json create mode 100644 .github/compatibility-test-matrices/unreleased/transfer-v2-channel-upgrade.json create mode 100644 .github/compatibility-test-matrices/unreleased/transfer-v2.json diff --git a/.github/compatibility-test-matrices/main/transfer-v2-chain-a.json b/.github/compatibility-test-matrices/main/transfer-v2-chain-a.json new file mode 100644 index 00000000000..ca3ff211d19 --- /dev/null +++ b/.github/compatibility-test-matrices/main/transfer-v2-chain-a.json @@ -0,0 +1,17 @@ +{ + "chain-a": [ + "main" + ], + "chain-b": [ + "main" + ], + "entrypoint": [ + "TestTransferTestSuite" + ], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom" + ], + "relayer-type": [ + "hermes" + ] +} \ No newline at end of file diff --git a/.github/compatibility-test-matrices/main/transfer-v2-channel-upgrade-chain-a.json b/.github/compatibility-test-matrices/main/transfer-v2-channel-upgrade-chain-a.json new file mode 100644 index 00000000000..9703bd94cef --- /dev/null +++ b/.github/compatibility-test-matrices/main/transfer-v2-channel-upgrade-chain-a.json @@ -0,0 +1,19 @@ +{ + "chain-a": [ + "main" + ], + "chain-b": [ + "main" + ], + "entrypoint": [ + "TestTransferChannelUpgradesTestSuite" + ], + "test": [ + "TestChannelUpgrade_WithICS20v2_Succeeds", + "TestChannelUpgrade_WithFeeMiddlewareAndICS20v2_Succeeds", + "TestChannelDowngrade_WithICS20v1_Succeeds" + ], + "relayer-type": [ + "hermes" + ] +} \ No newline at end of file diff --git a/.github/compatibility-test-matrices/unreleased/transfer-v2-channel-upgrade.json b/.github/compatibility-test-matrices/unreleased/transfer-v2-channel-upgrade.json new file mode 100644 index 00000000000..1d13a54e695 --- /dev/null +++ b/.github/compatibility-test-matrices/unreleased/transfer-v2-channel-upgrade.json @@ -0,0 +1,19 @@ +{ + "chain-a": [ + "release-v9.0.x" + ], + "chain-b": [ + "release-v9.0.x" + ], + "entrypoint": [ + "TestTransferChannelUpgradesTestSuite" + ], + "test": [ + "TestChannelUpgrade_WithICS20v2_Succeeds", + "TestChannelUpgrade_WithFeeMiddlewareAndICS20v2_Succeeds", + "TestChannelDowngrade_WithICS20v1_Succeeds" + ], + "relayer-type": [ + "hermes" + ] +} \ No newline at end of file diff --git a/.github/compatibility-test-matrices/unreleased/transfer-v2.json b/.github/compatibility-test-matrices/unreleased/transfer-v2.json new file mode 100644 index 00000000000..69b8b5f5efc --- /dev/null +++ b/.github/compatibility-test-matrices/unreleased/transfer-v2.json @@ -0,0 +1,17 @@ +{ + "chain-a": [ + "release-v9.0.x" + ], + "chain-b": [ + "release-v9.0.x" + ], + "entrypoint": [ + "TestTransferTestSuite" + ], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom" + ], + "relayer-type": [ + "hermes" + ] +} diff --git a/e2e/tests/core/03-connection/connection_test.go b/e2e/tests/core/03-connection/connection_test.go index 839d599fd2a..c9283be5abb 100644 --- a/e2e/tests/core/03-connection/connection_test.go +++ b/e2e/tests/core/03-connection/connection_test.go @@ -14,7 +14,6 @@ import ( test "github.com/strangelove-ventures/interchaintest/v8/testutil" testifysuite "github.com/stretchr/testify/suite" - sdk "github.com/cosmos/cosmos-sdk/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" paramsproposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal" diff --git a/e2e/tests/transfer/base_test.go b/e2e/tests/transfer/base_test.go index eb75de75f83..04bffa17175 100644 --- a/e2e/tests/transfer/base_test.go +++ b/e2e/tests/transfer/base_test.go @@ -235,7 +235,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom( s.Require().Equal(expected, actualBalance) }) - t.Run("chain B ibc denom", func(t *testing.T) { + t.Run("chain B IBC denom", func(t *testing.T) { actualBalance, err := query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) s.Require().NoError(err) diff --git a/e2e/tests/transfer/upgrades_test.go b/e2e/tests/transfer/upgrades_test.go index b3643a0230a..2bf3148c4e4 100644 --- a/e2e/tests/transfer/upgrades_test.go +++ b/e2e/tests/transfer/upgrades_test.go @@ -281,7 +281,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succee s.Require().Equal(transfertypes.V1, channel.Version, "the channel version is not ics20-1") }) - t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + t.Run("native token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { chainBWalletAmount := ibc.WalletAmount{ Address: chainBWallet.FormattedAddress(), // destination address Denom: chainA.Config().Denom, @@ -370,7 +370,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succee s.Require().Equal(expected, actualBalance) }) - t.Run("chain B ibc denom", func(t *testing.T) { + t.Run("chain B IBC denom", func(t *testing.T) { actualBalance, err := query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) s.Require().NoError(err) @@ -386,6 +386,188 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succee }) } +// TestChannelUpgrade_WithFeeMiddlewareAndICS20v2_Succeeds tests upgrading a transfer channel to wire up fee middleware and upgrade to ICS20 v2. +func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddlewareAndICS20v2_Succeeds() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, func(opts *ibc.CreateChannelOptions) { + opts.Version = transfertypes.V1 + opts.SourcePortName = transfertypes.PortID + opts.DestPortName = transfertypes.PortID + }) + + channelB := channelA.Counterparty + chainA, chainB := s.GetChains() + + chainADenom := chainA.Config().Denom + chainBDenom := chainB.Config().Denom + chainAIBCToken := testsuite.GetIBCToken(chainBDenom, channelA.PortID, channelA.ChannelID) + chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelB.PortID, channelB.ChannelID) + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.FormattedAddress() + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + var ( + err error + channel channeltypes.Channel + ) + + t.Run("verify transfer version of channel A is ics20-1", func(t *testing.T) { + channel, err = query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Equal(transfertypes.V1, channel.Version, "the channel version is not ics20-1") + }) + + t.Run("native token transfer from chainB to chainA, sender is source of tokens", func(t *testing.T) { + chainAwalletAmount := ibc.WalletAmount{ + Address: chainAWallet.FormattedAddress(), // destination address + Denom: chainBDenom, + Amount: sdkmath.NewInt(testvalues.IBCTransferAmount), + } + + transferTxResp, err := chainB.SendIBCTransfer(ctx, channelB.ChannelID, chainBWallet.KeyName(), chainAwalletAmount, ibc.TransferOptions{}) + s.Require().NoError(err) + s.Require().NoError(transferTxResp.Validate(), "chain-b ibc transfer tx is invalid") + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) { + channel.Version = transfertypes.V2 // change version to ics20-2 + s.InitiateChannelUpgrade(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, s.CreateUpgradeFields(channel)) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") + + t.Run("verify channel A upgraded and channel version is {ics29-1,ics20-2}", func(t *testing.T) { + channel, err = query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + + // check the channel version include the fee version + version, err := feetypes.MetadataFromVersion(channel.Version) + s.Require().NoError(err) + s.Require().Equal(feetypes.Version, version.FeeVersion, "the channel version did not include ics29") + s.Require().Equal(transfertypes.V2, version.AppVersion, "the channel version is not ics20-2") + }) + + t.Run("verify channel B upgraded and channel version is {ics29-1,ics20-2}", func(t *testing.T) { + channel, err = query.Channel(ctx, chainB, channelB.PortID, channelB.ChannelID) + s.Require().NoError(err) + + // check the channel version include the fee version + version, err := feetypes.MetadataFromVersion(channel.Version) + s.Require().NoError(err) + s.Require().Equal(feetypes.Version, version.FeeVersion, "the channel version did not include ics29") + s.Require().Equal(transfertypes.V2, version.AppVersion, "the channel version is not ics20-2") + }) + + var ( + chainARelayerWallet, chainBRelayerWallet ibc.Wallet + relayerAStartingBalance int64 + testFee = testvalues.DefaultFee(chainADenom) + ) + + t.Run("recover relayer wallets", func(t *testing.T) { + err := s.RecoverRelayerWallets(ctx, relayer) + s.Require().NoError(err) + + chainARelayerWallet, chainBRelayerWallet, err = s.GetRelayerWallets(relayer) + s.Require().NoError(err) + + relayerAStartingBalance, err = s.GetChainANativeBalance(ctx, chainARelayerWallet) + s.Require().NoError(err) + t.Logf("relayer A user starting with balance: %d", relayerAStartingBalance) + }) + + t.Run("register and verify counterparty payee", func(t *testing.T) { + _, chainBRelayerUser := s.GetRelayerUsers(ctx) + resp := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.AssertTxSuccess(resp) + + address, err := query.CounterPartyPayee(ctx, chainB, chainBRelayerWallet.FormattedAddress(), channelA.Counterparty.ChannelID) + s.Require().NoError(err) + s.Require().Equal(chainARelayerWallet.FormattedAddress(), address) + }) + + // send the native chainA denom and also the ibc token from chainB + denoms := []string{chainAIBCToken.IBCDenom(), chainADenom} + var transferCoins []sdk.Coin + for _, denom := range denoms { + transferCoins = append(transferCoins, testvalues.DefaultTransferAmount(denom)) + } + + t.Run("send incentivized transfer packet to chain B with native token from chain A and non-native IBC token from chainB", func(t *testing.T) { + // before adding fees for the packet, there should not be incentivized packets + packets, err := query.IncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + + msgPayPacketFee := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.FormattedAddress(), nil) + msgTransfer := testsuite.GetMsgTransfer( + channelA.PortID, + channelA.ChannelID, + transfertypes.V2, + transferCoins, + chainAWallet.FormattedAddress(), + chainBWallet.FormattedAddress(), + s.GetTimeoutHeight(ctx, chainB), + 0, + "", + ) + resp := s.BroadcastMessages(ctx, chainA, chainAWallet, msgPayPacketFee, msgTransfer) + s.AssertTxSuccess(resp) + }) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + t.Run("chain B native denom", func(t *testing.T) { + actualBalance, err := s.GetChainBNativeBalance(ctx, chainBWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("chain A IBC denom", func(t *testing.T) { + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + }) + + t.Run("timeout fee is refunded", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + // once the relayer has relayed the packets, the timeout fee should be refunded. + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount - testFee.AckFee.AmountOf(chainADenom).Int64() - testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + + t.Run("relayerA is paid ack and recv fee", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainARelayerWallet) + s.Require().NoError(err) + + expected := relayerAStartingBalance + testFee.AckFee.AmountOf(chainADenom).Int64() + testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + + t.Run("tokens are un-escrowed", func(t *testing.T) { + actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainB, chainADenom) + s.Require().NoError(err) + s.Require().Equal(sdk.NewCoin(chainADenom, sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because tokens have come back + }) +} + // TestChannelDowngrade_WithICS20v1_Succeeds tests downgrading a transfer channel from ICS20 v2 to ICS20 v1. func (s *TransferChannelUpgradesTestSuite) TestChannelDowngrade_WithICS20v1_Succeeds() { t := s.T() @@ -405,8 +587,13 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelDowngrade_WithICS20v1_Succ s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + var ( + err error + channel channeltypes.Channel + ) + t.Run("verify transfer version of channel A is ics20-2", func(t *testing.T) { - channel, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + channel, err = query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) s.Require().NoError(err) s.Require().Equal(transfertypes.V2, channel.Version, "the channel version is not ics20-2") }) @@ -416,23 +603,20 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelDowngrade_WithICS20v1_Succ }) t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) { - chA, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) - s.Require().NoError(err) - - upgradeFields := channeltypes.NewUpgradeFields(chA.Ordering, chA.ConnectionHops, transfertypes.V1) + upgradeFields := channeltypes.NewUpgradeFields(channel.Ordering, channel.ConnectionHops, transfertypes.V1) s.InitiateChannelUpgrade(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, upgradeFields) }) s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") t.Run("verify channel A downgraded and transfer version is ics20-1", func(t *testing.T) { - channel, err := query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) + channel, err = query.Channel(ctx, chainA, channelA.PortID, channelA.ChannelID) s.Require().NoError(err) s.Require().Equal(transfertypes.V1, channel.Version, "the channel version is not ics20-1") }) t.Run("verify channel B downgraded and transfer version is ics20-1", func(t *testing.T) { - channel, err := query.Channel(ctx, chainB, channelB.PortID, channelB.ChannelID) + channel, err = query.Channel(ctx, chainB, channelB.PortID, channelB.ChannelID) s.Require().NoError(err) s.Require().Equal(transfertypes.V1, channel.Version, "the channel version is not ics20-1") }) From 3f12a8e95458a7493c3302e3b3978a43485130e0 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 10 Jun 2024 09:02:46 +0200 Subject: [PATCH 38/42] revert some unnecessary changes --- e2e/tests/transfer/base_test.go | 2 +- e2e/tests/upgrades/upgrade_test.go | 2 +- e2e/testsuite/testsuite.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/e2e/tests/transfer/base_test.go b/e2e/tests/transfer/base_test.go index 04bffa17175..03e90536d3c 100644 --- a/e2e/tests/transfer/base_test.go +++ b/e2e/tests/transfer/base_test.go @@ -409,7 +409,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Timeout_Nonincentivized() { t := s.T() ctx := context.TODO() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, nil) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions()) chainA, _ := s.GetChains() chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) diff --git a/e2e/tests/upgrades/upgrade_test.go b/e2e/tests/upgrades/upgrade_test.go index 3631d1ae66e..942a11d18ca 100644 --- a/e2e/tests/upgrades/upgrade_test.go +++ b/e2e/tests/upgrades/upgrade_test.go @@ -747,7 +747,7 @@ func (s *UpgradeTestSuite) TestV8ToV8_1ChainUpgrade_ChannelUpgrades() { testCfg := testsuite.LoadConfig() ctx := context.Background() - relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, nil) + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions()) channelB := channelA.Counterparty chainA, chainB := s.GetChains() diff --git a/e2e/testsuite/testsuite.go b/e2e/testsuite/testsuite.go index bf879701ef2..b678850084b 100644 --- a/e2e/testsuite/testsuite.go +++ b/e2e/testsuite/testsuite.go @@ -511,7 +511,7 @@ func (s *E2ETestSuite) GetTimeoutHeight(ctx context.Context, chain ibc.Chain) cl return clienttypes.NewHeight(clienttypes.ParseChainID(chain.Config().ChainID), uint64(height)+1000) } -// CreateUpgradeFields creates upgrade fields for channel with fee middleware. +// CreateUpgradeFields creates upgrade fields for channel with fee middleware func (s *E2ETestSuite) CreateUpgradeFields(channel channeltypes.Channel) channeltypes.UpgradeFields { versionMetadata := feetypes.Metadata{ FeeVersion: feetypes.Version, From 087d52fe3c4a4740ae354c89f0d87cbf0d371009 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 10 Jun 2024 10:09:56 +0200 Subject: [PATCH 39/42] add transfer failure multidenom test to compatibility --- .../main/transfer-v2-chain-a.json | 3 ++- .../compatibility-test-matrices/unreleased/transfer-v2.json | 3 ++- e2e/tests/transfer/base_test.go | 6 +----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/compatibility-test-matrices/main/transfer-v2-chain-a.json b/.github/compatibility-test-matrices/main/transfer-v2-chain-a.json index ca3ff211d19..95ca1fcfe0f 100644 --- a/.github/compatibility-test-matrices/main/transfer-v2-chain-a.json +++ b/.github/compatibility-test-matrices/main/transfer-v2-chain-a.json @@ -9,7 +9,8 @@ "TestTransferTestSuite" ], "test": [ - "TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom" + "TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom", + "TestMsgTransfer_Fails_InvalidAddress_MultiDenom" ], "relayer-type": [ "hermes" diff --git a/.github/compatibility-test-matrices/unreleased/transfer-v2.json b/.github/compatibility-test-matrices/unreleased/transfer-v2.json index 69b8b5f5efc..23484ea3cd0 100644 --- a/.github/compatibility-test-matrices/unreleased/transfer-v2.json +++ b/.github/compatibility-test-matrices/unreleased/transfer-v2.json @@ -9,7 +9,8 @@ "TestTransferTestSuite" ], "test": [ - "TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom" + "TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom", + "TestMsgTransfer_Fails_InvalidAddress_MultiDenom" ], "relayer-type": [ "hermes" diff --git a/e2e/tests/transfer/base_test.go b/e2e/tests/transfer/base_test.go index 03e90536d3c..8e197858180 100644 --- a/e2e/tests/transfer/base_test.go +++ b/e2e/tests/transfer/base_test.go @@ -332,15 +332,11 @@ func (s *TransferTestSuite) TestMsgTransfer_Fails_InvalidAddress_MultiDenom() { 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) }) - t.Run("token are returned to sender on chainB", func(t *testing.T) { + t.Run("tokens are returned to sender on chainB", func(t *testing.T) { t.Run("non-native chainA ibc denom", func(t *testing.T) { actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) s.Require().NoError(err) From bdd4ab66abe31ab60c32486ae2b32d3712710b80 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 17 Jun 2024 09:37:43 +0200 Subject: [PATCH 40/42] updates to multidenom invalid adress test --- e2e/tests/transfer/base_test.go | 44 ++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/e2e/tests/transfer/base_test.go b/e2e/tests/transfer/base_test.go index 8e197858180..2bbf29d7f16 100644 --- a/e2e/tests/transfer/base_test.go +++ b/e2e/tests/transfer/base_test.go @@ -317,6 +317,10 @@ func (s *TransferTestSuite) TestMsgTransfer_Fails_InvalidAddress_MultiDenom() { transferCoins = append(transferCoins, testvalues.DefaultTransferAmount(denom)) } + t.Run("stop relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + }) + t.Run("native token from chain B and non-native IBC token from chainA, both to chainA", func(t *testing.T) { transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, transferCoins, chainBAddress, testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") s.AssertTxSuccess(transferTxResp) @@ -324,27 +328,31 @@ func (s *TransferTestSuite) TestMsgTransfer_Fails_InvalidAddress_MultiDenom() { s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") - t.Run("native chainB tokens are escrowed", func(t *testing.T) { - actualBalance, err := s.GetChainBNativeBalance(ctx, chainBWallet) - s.Require().NoError(err) + t.Run("tokens are sent from chain B", func(t *testing.T) { + t.Run("native chainB tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainBNativeBalance(ctx, chainBWallet) + s.Require().NoError(err) - expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount - s.Require().Equal(expected, actualBalance) + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("non-native chainA IBC denom are burned", func(t *testing.T) { + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + s.Require().Equal(0, actualBalance.Int64()) + }) + }) + + 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) + s.AssertPacketRelayed(ctx, chainB, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) }) t.Run("tokens are returned to sender on chainB", func(t *testing.T) { - t.Run("non-native chainA ibc denom", func(t *testing.T) { - actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) - s.Require().NoError(err) - - expected := testvalues.IBCTransferAmount - s.Require().Equal(expected, actualBalance.Int64()) - }) - t.Run("native chainB denom", func(t *testing.T) { actualBalance, err := s.GetChainBNativeBalance(ctx, chainBWallet) s.Require().NoError(err) @@ -352,6 +360,14 @@ func (s *TransferTestSuite) TestMsgTransfer_Fails_InvalidAddress_MultiDenom() { expected := testvalues.StartingTokenAmount s.Require().Equal(expected, actualBalance) }) + + t.Run("non-native chainA IBC denom", func(t *testing.T) { + actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) }) } From 7d42ff36cd9096163e5150d375fe1eee866b4522 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 17 Jun 2024 10:22:49 +0200 Subject: [PATCH 41/42] fix value comparison --- e2e/tests/transfer/base_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/tests/transfer/base_test.go b/e2e/tests/transfer/base_test.go index 2bbf29d7f16..dec6a35d085 100644 --- a/e2e/tests/transfer/base_test.go +++ b/e2e/tests/transfer/base_test.go @@ -340,7 +340,7 @@ func (s *TransferTestSuite) TestMsgTransfer_Fails_InvalidAddress_MultiDenom() { t.Run("non-native chainA IBC denom are burned", func(t *testing.T) { actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) s.Require().NoError(err) - s.Require().Equal(0, actualBalance.Int64()) + s.Require().Equal(int64(0), actualBalance.Int64()) }) }) From d4bd933d8dbfb8b0814666006d001d67395491f2 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 17 Jun 2024 15:40:57 +0200 Subject: [PATCH 42/42] review comments --- e2e/tests/transfer/base_test.go | 14 ++++++-------- e2e/tests/transfer/upgrades_test.go | 23 ++++++++++++++--------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/e2e/tests/transfer/base_test.go b/e2e/tests/transfer/base_test.go index dec6a35d085..d11c45c5798 100644 --- a/e2e/tests/transfer/base_test.go +++ b/e2e/tests/transfer/base_test.go @@ -211,10 +211,9 @@ func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom( }) // send the native chainB denom and also the ibc token from chainA - denoms := []string{chainBIBCToken.IBCDenom(), chainBDenom} - var transferCoins []sdk.Coin - for _, denom := range denoms { - transferCoins = append(transferCoins, testvalues.DefaultTransferAmount(denom)) + transferCoins := []sdk.Coin{ + testvalues.DefaultTransferAmount(chainBIBCToken.IBCDenom()), + testvalues.DefaultTransferAmount(chainBDenom), } t.Run("native token from chain B and non-native IBC token from chainA, both to chainA", func(t *testing.T) { @@ -311,10 +310,9 @@ func (s *TransferTestSuite) TestMsgTransfer_Fails_InvalidAddress_MultiDenom() { }) // send the native chainB denom and also the ibc token from chainA - denoms := []string{chainBIBCToken.IBCDenom(), chainBDenom} - var transferCoins []sdk.Coin - for _, denom := range denoms { - transferCoins = append(transferCoins, testvalues.DefaultTransferAmount(denom)) + transferCoins := []sdk.Coin{ + testvalues.DefaultTransferAmount(chainBIBCToken.IBCDenom()), + testvalues.DefaultTransferAmount(chainBDenom), } t.Run("stop relayer", func(t *testing.T) { diff --git a/e2e/tests/transfer/upgrades_test.go b/e2e/tests/transfer/upgrades_test.go index 2bf3148c4e4..c458e71cf96 100644 --- a/e2e/tests/transfer/upgrades_test.go +++ b/e2e/tests/transfer/upgrades_test.go @@ -255,8 +255,6 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succee relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, func(opts *ibc.CreateChannelOptions) { opts.Version = transfertypes.V1 - opts.SourcePortName = transfertypes.PortID - opts.DestPortName = transfertypes.PortID }) channelB := channelA.Counterparty @@ -346,10 +344,9 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succee }) // send the native chainB denom and also the ibc token from chainA - denoms := []string{chainBIBCToken.IBCDenom(), chainBDenom} - var transferCoins []sdk.Coin - for _, denom := range denoms { - transferCoins = append(transferCoins, testvalues.DefaultTransferAmount(denom)) + transferCoins := []sdk.Coin{ + testvalues.DefaultTransferAmount(chainBIBCToken.IBCDenom()), + testvalues.DefaultTransferAmount(chainBDenom), } t.Run("native token from chain B and non-native IBC token from chainA, both to chainA", func(t *testing.T) { @@ -380,9 +377,17 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithICS20v2_Succee }) t.Run("tokens are un-escrowed", func(t *testing.T) { - actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) - s.Require().NoError(err) - s.Require().Equal(sdk.NewCoin(chainADenom, sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because tokens have come back + t.Run("chain A escrow", func(t *testing.T) { + actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) + s.Require().NoError(err) + s.Require().Equal(sdk.NewCoin(chainADenom, sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because tokens have come back + }) + + t.Run("chain B escrow", func(t *testing.T) { + actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainB, chainBDenom) + s.Require().NoError(err) + s.Require().Equal(sdk.NewCoin(chainBDenom, sdkmath.NewInt(testvalues.IBCTransferAmount)), actualTotalEscrow) + }) }) }