ibc: TimeoutOnClose support (#7066)

* ibc: TimeoutOnClose support

* revert added callback

* update proto

* update spec and add msg test

* update handler and add test

* apply self-review changes

* apply @fedekunze review suggestion

Co-authored-by: Colin Axner <colinaxner@berkeley.edu>
Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com>
This commit is contained in:
Federico Kunze
2020-08-20 07:19:57 -04:00
committed by GitHub
co-authored by Colin Axner colin axnér
parent b699c9459f
commit 16435a0f4d
14 changed files with 1056 additions and 194 deletions
+2 -2
View File
@@ -38,7 +38,7 @@ func (suite *HandlerTestSuite) TestHandleMsgTransfer() {
// send from chainA to chainB
msg := types.NewMsgTransfer(channelA.PortID, channelA.ID, coinToSendToB, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), 110, 0)
err := suite.coordinator.SendMsgs(suite.chainA, suite.chainB, clientB, msg)
err := suite.coordinator.SendMsg(suite.chainA, suite.chainB, clientB, msg)
suite.Require().NoError(err) // message committed
// relay send
@@ -58,7 +58,7 @@ func (suite *HandlerTestSuite) TestHandleMsgTransfer() {
// send from chainB back to chainA
msg = types.NewMsgTransfer(channelB.PortID, channelB.ID, coinToSendBackToA, suite.chainB.SenderAccount.GetAddress(), suite.chainA.SenderAccount.GetAddress().String(), 110, 0)
err = suite.coordinator.SendMsgs(suite.chainB, suite.chainA, clientA, msg)
err = suite.coordinator.SendMsg(suite.chainB, suite.chainA, clientA, msg)
suite.Require().NoError(err) // message committed
// relay send
+9 -1
View File
@@ -175,7 +175,7 @@ func (am AppModule) WeightedOperations(_ module.SimulationState) []simtypes.Weig
//____________________________________________________________________________
// Implement IBCModule callbacks
// OnChanOpenInit implements the IBCModule interface
func (am AppModule) OnChanOpenInit(
ctx sdk.Context,
order channeltypes.Order,
@@ -207,6 +207,7 @@ func (am AppModule) OnChanOpenInit(
return nil
}
// OnChanOpenTry implements the IBCModule interface
func (am AppModule) OnChanOpenTry(
ctx sdk.Context,
order channeltypes.Order,
@@ -243,6 +244,7 @@ func (am AppModule) OnChanOpenTry(
return nil
}
// OnChanOpenAck implements the IBCModule interface
func (am AppModule) OnChanOpenAck(
ctx sdk.Context,
portID,
@@ -255,6 +257,7 @@ func (am AppModule) OnChanOpenAck(
return nil
}
// OnChanOpenConfirm implements the IBCModule interface
func (am AppModule) OnChanOpenConfirm(
ctx sdk.Context,
portID,
@@ -263,6 +266,7 @@ func (am AppModule) OnChanOpenConfirm(
return nil
}
// OnChanCloseInit implements the IBCModule interface
func (am AppModule) OnChanCloseInit(
ctx sdk.Context,
portID,
@@ -272,6 +276,7 @@ func (am AppModule) OnChanCloseInit(
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "user cannot close channel")
}
// OnChanCloseConfirm implements the IBCModule interface
func (am AppModule) OnChanCloseConfirm(
ctx sdk.Context,
portID,
@@ -280,6 +285,7 @@ func (am AppModule) OnChanCloseConfirm(
return nil
}
// OnRecvPacket implements the IBCModule interface
func (am AppModule) OnRecvPacket(
ctx sdk.Context,
packet channeltypes.Packet,
@@ -316,6 +322,7 @@ func (am AppModule) OnRecvPacket(
}, acknowledgement.GetBytes(), nil
}
// OnAcknowledgementPacket implements the IBCModule interface
func (am AppModule) OnAcknowledgementPacket(
ctx sdk.Context,
packet channeltypes.Packet,
@@ -359,6 +366,7 @@ func (am AppModule) OnAcknowledgementPacket(
}, nil
}
// OnTimeoutPacket implements the IBCModule interface
func (am AppModule) OnTimeoutPacket(
ctx sdk.Context,
packet channeltypes.Packet,
+2 -24
View File
@@ -145,7 +145,7 @@ func (k Keeper) TimeoutExecuted(
k.SetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), channel)
}
k.Logger(ctx).Info(fmt.Sprintf("packet timed-out: %v", packet))
k.Logger(ctx).Info("packet timed-out", "packet", packet)
// emit an event marking that we have processed the timeout
ctx.EventManager().EmitEvents(sdk.Events{
@@ -266,28 +266,6 @@ func (k Keeper) TimeoutOnClose(
return err
}
k.deletePacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
k.Logger(ctx).Info(fmt.Sprintf("packet timed-out on close: %v", packet))
// emit an event marking that we have processed the timeout
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeTimeoutPacket,
sdk.NewAttribute(types.AttributeKeyTimeoutHeight, fmt.Sprintf("%d", packet.GetTimeoutHeight())),
sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())),
sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())),
sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()),
sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()),
sdk.NewAttribute(types.AttributeKeyDstPort, packet.GetDestPort()),
sdk.NewAttribute(types.AttributeKeyDstChannel, packet.GetDestChannel()),
sdk.NewAttribute(types.AttributeKeyChannelOrdering, channel.Ordering.String()),
),
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
),
})
// NOTE: the remaining code is located in the TimeoutExecuted function
return nil
}
+506 -96
View File
@@ -38,7 +38,8 @@ const (
// A channel has completed the handshake. Open channels are
// ready to send and receive packets.
OPEN State = 3
// A channel has been closed and can no longer be used to send or receive packets.
// A channel has been closed and can no longer be used to send or receive
// packets.
CLOSED State = 4
)
@@ -72,7 +73,8 @@ type Order int32
const (
// zero-value for channel ordering
NONE Order = 0
// packets can be delivered in any order, which may differ from the order in which they were sent.
// packets can be delivered in any order, which may differ from the order in
// which they were sent.
UNORDERED Order = 1
// packets are delivered exactly in the order which they were sent
ORDERED Order = 2
@@ -98,8 +100,8 @@ func (Order) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_9277922ccfb7f043, []int{1}
}
// MsgChannelOpenInit defines an sdk.Msg to initialize a channel handshake. It is
// called by a relayer on Chain A.
// MsgChannelOpenInit defines an sdk.Msg to initialize a channel handshake. It
// is called by a relayer on Chain A.
type MsgChannelOpenInit struct {
PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"`
ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"`
@@ -348,8 +350,8 @@ func (m *MsgChannelOpenAck) GetSigner() github_com_cosmos_cosmos_sdk_types.AccAd
return nil
}
// MsgChannelOpenConfirm defines a msg sent by a Relayer to Chain B to acknowledge
// the change of channel state to OPEN on Chain A.
// MsgChannelOpenConfirm defines a msg sent by a Relayer to Chain B to
// acknowledge the change of channel state to OPEN on Chain A.
type MsgChannelOpenConfirm struct {
PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"`
ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"`
@@ -712,6 +714,91 @@ func (m *MsgTimeout) GetSigner() github_com_cosmos_cosmos_sdk_types.AccAddress {
return nil
}
// MsgTimeoutOnClose timed-out packet upon counterparty channel closure.
type MsgTimeoutOnClose struct {
Packet Packet `protobuf:"bytes,1,opt,name=packet,proto3" json:"packet"`
Proof []byte `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"`
ProofClose []byte `protobuf:"bytes,3,opt,name=proof_close,json=proofClose,proto3" json:"proof_close,omitempty" yaml:"proof_close"`
ProofHeight uint64 `protobuf:"varint,4,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height,omitempty" yaml:"proof_height"`
NextSequenceRecv uint64 `protobuf:"varint,5,opt,name=next_sequence_recv,json=nextSequenceRecv,proto3" json:"next_sequence_recv,omitempty" yaml:"next_sequence_recv"`
Signer github_com_cosmos_cosmos_sdk_types.AccAddress `protobuf:"bytes,6,opt,name=signer,proto3,casttype=github.com/cosmos/cosmos-sdk/types.AccAddress" json:"signer,omitempty"`
}
func (m *MsgTimeoutOnClose) Reset() { *m = MsgTimeoutOnClose{} }
func (m *MsgTimeoutOnClose) String() string { return proto.CompactTextString(m) }
func (*MsgTimeoutOnClose) ProtoMessage() {}
func (*MsgTimeoutOnClose) Descriptor() ([]byte, []int) {
return fileDescriptor_9277922ccfb7f043, []int{8}
}
func (m *MsgTimeoutOnClose) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MsgTimeoutOnClose) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_MsgTimeoutOnClose.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 *MsgTimeoutOnClose) XXX_Merge(src proto.Message) {
xxx_messageInfo_MsgTimeoutOnClose.Merge(m, src)
}
func (m *MsgTimeoutOnClose) XXX_Size() int {
return m.Size()
}
func (m *MsgTimeoutOnClose) XXX_DiscardUnknown() {
xxx_messageInfo_MsgTimeoutOnClose.DiscardUnknown(m)
}
var xxx_messageInfo_MsgTimeoutOnClose proto.InternalMessageInfo
func (m *MsgTimeoutOnClose) GetPacket() Packet {
if m != nil {
return m.Packet
}
return Packet{}
}
func (m *MsgTimeoutOnClose) GetProof() []byte {
if m != nil {
return m.Proof
}
return nil
}
func (m *MsgTimeoutOnClose) GetProofClose() []byte {
if m != nil {
return m.ProofClose
}
return nil
}
func (m *MsgTimeoutOnClose) GetProofHeight() uint64 {
if m != nil {
return m.ProofHeight
}
return 0
}
func (m *MsgTimeoutOnClose) GetNextSequenceRecv() uint64 {
if m != nil {
return m.NextSequenceRecv
}
return 0
}
func (m *MsgTimeoutOnClose) GetSigner() github_com_cosmos_cosmos_sdk_types.AccAddress {
if m != nil {
return m.Signer
}
return nil
}
// MsgAcknowledgement receives incoming IBC acknowledgement
type MsgAcknowledgement struct {
Packet Packet `protobuf:"bytes,1,opt,name=packet,proto3" json:"packet"`
@@ -725,7 +812,7 @@ func (m *MsgAcknowledgement) Reset() { *m = MsgAcknowledgement{} }
func (m *MsgAcknowledgement) String() string { return proto.CompactTextString(m) }
func (*MsgAcknowledgement) ProtoMessage() {}
func (*MsgAcknowledgement) Descriptor() ([]byte, []int) {
return fileDescriptor_9277922ccfb7f043, []int{8}
return fileDescriptor_9277922ccfb7f043, []int{9}
}
func (m *MsgAcknowledgement) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -790,8 +877,8 @@ func (m *MsgAcknowledgement) GetSigner() github_com_cosmos_cosmos_sdk_types.AccA
}
// Channel defines pipeline for exactly-once packet delivery between specific
// modules on separate blockchains, which has at least one end capable of sending
// packets and one end capable of receiving packets.
// modules on separate blockchains, which has at least one end capable of
// sending packets and one end capable of receiving packets.
type Channel struct {
// current state of the channel end
State State `protobuf:"varint,1,opt,name=state,proto3,enum=ibc.channel.State" json:"state,omitempty"`
@@ -810,7 +897,7 @@ func (m *Channel) Reset() { *m = Channel{} }
func (m *Channel) String() string { return proto.CompactTextString(m) }
func (*Channel) ProtoMessage() {}
func (*Channel) Descriptor() ([]byte, []int) {
return fileDescriptor_9277922ccfb7f043, []int{9}
return fileDescriptor_9277922ccfb7f043, []int{10}
}
func (m *Channel) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -839,8 +926,8 @@ func (m *Channel) XXX_DiscardUnknown() {
var xxx_messageInfo_Channel proto.InternalMessageInfo
// IdentifiedChannel defines a channel with additional port and channel identifier
// fields.
// IdentifiedChannel defines a channel with additional port and channel
// identifier fields.
type IdentifiedChannel struct {
// current state of the channel end
State State `protobuf:"varint,1,opt,name=state,proto3,enum=ibc.channel.State" json:"state,omitempty"`
@@ -863,7 +950,7 @@ func (m *IdentifiedChannel) Reset() { *m = IdentifiedChannel{} }
func (m *IdentifiedChannel) String() string { return proto.CompactTextString(m) }
func (*IdentifiedChannel) ProtoMessage() {}
func (*IdentifiedChannel) Descriptor() ([]byte, []int) {
return fileDescriptor_9277922ccfb7f043, []int{10}
return fileDescriptor_9277922ccfb7f043, []int{11}
}
func (m *IdentifiedChannel) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -904,7 +991,7 @@ func (m *Counterparty) Reset() { *m = Counterparty{} }
func (m *Counterparty) String() string { return proto.CompactTextString(m) }
func (*Counterparty) ProtoMessage() {}
func (*Counterparty) Descriptor() ([]byte, []int) {
return fileDescriptor_9277922ccfb7f043, []int{11}
return fileDescriptor_9277922ccfb7f043, []int{12}
}
func (m *Counterparty) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -959,7 +1046,7 @@ func (m *Packet) Reset() { *m = Packet{} }
func (m *Packet) String() string { return proto.CompactTextString(m) }
func (*Packet) ProtoMessage() {}
func (*Packet) Descriptor() ([]byte, []int) {
return fileDescriptor_9277922ccfb7f043, []int{12}
return fileDescriptor_9277922ccfb7f043, []int{13}
}
func (m *Packet) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1005,7 +1092,7 @@ func (m *PacketAckCommitment) Reset() { *m = PacketAckCommitment{} }
func (m *PacketAckCommitment) String() string { return proto.CompactTextString(m) }
func (*PacketAckCommitment) ProtoMessage() {}
func (*PacketAckCommitment) Descriptor() ([]byte, []int) {
return fileDescriptor_9277922ccfb7f043, []int{13}
return fileDescriptor_9277922ccfb7f043, []int{14}
}
func (m *PacketAckCommitment) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1045,6 +1132,7 @@ func init() {
proto.RegisterType((*MsgChannelCloseConfirm)(nil), "ibc.channel.MsgChannelCloseConfirm")
proto.RegisterType((*MsgRecvPacket)(nil), "ibc.channel.MsgRecvPacket")
proto.RegisterType((*MsgTimeout)(nil), "ibc.channel.MsgTimeout")
proto.RegisterType((*MsgTimeoutOnClose)(nil), "ibc.channel.MsgTimeoutOnClose")
proto.RegisterType((*MsgAcknowledgement)(nil), "ibc.channel.MsgAcknowledgement")
proto.RegisterType((*Channel)(nil), "ibc.channel.Channel")
proto.RegisterType((*IdentifiedChannel)(nil), "ibc.channel.IdentifiedChannel")
@@ -1056,86 +1144,89 @@ func init() {
func init() { proto.RegisterFile("ibc/channel/channel.proto", fileDescriptor_9277922ccfb7f043) }
var fileDescriptor_9277922ccfb7f043 = []byte{
// 1259 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x58, 0x3f, 0x6f, 0xdb, 0x46,
0x14, 0x17, 0x25, 0x4a, 0xb2, 0x9e, 0xff, 0xc9, 0x67, 0xc7, 0x91, 0x95, 0x44, 0x14, 0x88, 0x0e,
0x42, 0x8a, 0xc8, 0xcd, 0x1f, 0xb4, 0x40, 0xa6, 0x4a, 0xb2, 0x82, 0x08, 0x8d, 0x25, 0xe3, 0xac,
0x14, 0x68, 0x16, 0x81, 0xa6, 0x2e, 0x12, 0x21, 0x8b, 0xa7, 0x92, 0x74, 0x12, 0xef, 0x1d, 0x02,
0x03, 0x2d, 0xfa, 0x05, 0x0c, 0x14, 0xe8, 0xd4, 0xa5, 0x53, 0x81, 0x76, 0xe9, 0xd4, 0x25, 0x5b,
0x33, 0x76, 0x62, 0x8a, 0xf8, 0x1b, 0x70, 0x2a, 0x3a, 0x15, 0xbc, 0x3b, 0x4a, 0xa4, 0x9c, 0x66,
0xb0, 0x0a, 0x25, 0x43, 0x27, 0xde, 0x7b, 0xef, 0x77, 0x77, 0xef, 0xfd, 0xde, 0xf3, 0x7b, 0x67,
0xc1, 0x96, 0x71, 0xa0, 0x6f, 0xeb, 0x7d, 0xcd, 0x34, 0xc9, 0x61, 0xf0, 0x2d, 0x8f, 0x2c, 0xea,
0x50, 0xb4, 0x68, 0x1c, 0xe8, 0x65, 0xa1, 0xca, 0x6f, 0xf4, 0x68, 0x8f, 0x32, 0xfd, 0xb6, 0xbf,
0xe2, 0x10, 0xf5, 0x2f, 0x09, 0xd0, 0xae, 0xdd, 0xab, 0x71, 0x50, 0x6b, 0x44, 0xcc, 0x86, 0x69,
0x38, 0xe8, 0x43, 0x48, 0x8f, 0xa8, 0xe5, 0x74, 0x8c, 0x6e, 0x4e, 0x2a, 0x4a, 0xa5, 0x4c, 0x15,
0x79, 0xae, 0xb2, 0x72, 0xac, 0x0d, 0x0f, 0xef, 0xaa, 0xc2, 0xa0, 0xe2, 0x94, 0xbf, 0x6a, 0x74,
0xd1, 0x1d, 0x00, 0x71, 0x89, 0x8f, 0x8f, 0x33, 0xfc, 0x25, 0xcf, 0x55, 0xd6, 0x38, 0x7e, 0x62,
0x53, 0x71, 0x46, 0x08, 0x6c, 0x57, 0x5a, 0x08, 0xb9, 0x44, 0x51, 0x2a, 0x2d, 0xde, 0xda, 0x28,
0x87, 0xdc, 0x2d, 0x0b, 0x8f, 0xaa, 0xf2, 0x0b, 0x57, 0x89, 0xe1, 0x00, 0x8a, 0x1a, 0x90, 0xb2,
0x8d, 0x9e, 0x49, 0xac, 0x9c, 0x5c, 0x94, 0x4a, 0x4b, 0xd5, 0x9b, 0x7f, 0xbb, 0xca, 0x8d, 0x9e,
0xe1, 0xf4, 0x8f, 0x0e, 0xca, 0x3a, 0x1d, 0x6e, 0xeb, 0xd4, 0x1e, 0x52, 0x5b, 0x7c, 0x6e, 0xd8,
0xdd, 0xc1, 0xb6, 0x73, 0x3c, 0x22, 0x76, 0xb9, 0xa2, 0xeb, 0x95, 0x6e, 0xd7, 0x22, 0xb6, 0x8d,
0xc5, 0x01, 0xea, 0xaf, 0x09, 0x58, 0x8b, 0x86, 0xde, 0xb6, 0x8e, 0xdf, 0xdf, 0xc8, 0x31, 0x6c,
0xe8, 0xf4, 0xc8, 0x74, 0x88, 0x35, 0xd2, 0x2c, 0xe7, 0xb8, 0xf3, 0x84, 0x58, 0xb6, 0x41, 0x4d,
0xc6, 0x43, 0xa6, 0xaa, 0x78, 0xae, 0x72, 0x45, 0xdc, 0xfa, 0x06, 0x94, 0x8a, 0xd7, 0xc3, 0xea,
0xcf, 0xb9, 0xd6, 0xf7, 0x7f, 0x64, 0x51, 0xfa, 0xb8, 0x63, 0x98, 0x86, 0x93, 0x4b, 0x32, 0x46,
0x43, 0xfe, 0x4f, 0x6c, 0x2a, 0xce, 0x30, 0x81, 0x15, 0xc7, 0x5d, 0x58, 0xe2, 0x96, 0x3e, 0x31,
0x7a, 0x7d, 0x27, 0x97, 0x2a, 0x4a, 0x25, 0xb9, 0x7a, 0xd9, 0x73, 0x95, 0xf5, 0xf0, 0x3e, 0x6e,
0x55, 0xf1, 0x22, 0x13, 0xef, 0x33, 0x29, 0x94, 0xbf, 0xf4, 0xac, 0xf9, 0xfb, 0xea, 0x5c, 0xfe,
0x2a, 0xfa, 0x60, 0x1e, 0xf9, 0xfb, 0xb7, 0x4c, 0x24, 0x66, 0xc8, 0xc4, 0x4d, 0xe0, 0x04, 0x77,
0x1c, 0xeb, 0x58, 0x94, 0xf6, 0x86, 0xe7, 0x2a, 0xd9, 0x30, 0xa1, 0x8e, 0x75, 0xac, 0xe2, 0x05,
0xb6, 0xf6, 0x2b, 0x75, 0x3a, 0x0d, 0xc9, 0x0b, 0xa5, 0x21, 0x35, 0x6b, 0x1a, 0x7e, 0x8a, 0xc3,
0xa5, 0x68, 0x1a, 0x6a, 0xd4, 0x7c, 0x6c, 0x58, 0xc3, 0x79, 0xa4, 0x62, 0x4c, 0x9b, 0xa6, 0x0f,
0x18, 0xff, 0x6f, 0xa0, 0x4d, 0xd3, 0x07, 0x01, 0x6d, 0x7e, 0x81, 0x4c, 0xd3, 0x26, 0x5f, 0x88,
0xb6, 0xe4, 0xac, 0xb4, 0xfd, 0x26, 0xc1, 0xfa, 0x84, 0xb6, 0xda, 0x21, 0xb5, 0xc9, 0xbc, 0x3a,
0xef, 0x24, 0x8a, 0xc4, 0xac, 0x51, 0xfc, 0x12, 0x87, 0xcd, 0xa9, 0x28, 0xe6, 0x98, 0xfd, 0x68,
0xfb, 0x4a, 0x5c, 0xb0, 0x7d, 0xbd, 0xa3, 0x02, 0x78, 0x25, 0xc1, 0xf2, 0xae, 0xdd, 0xc3, 0x44,
0x7f, 0xb2, 0xa7, 0xe9, 0x03, 0xe2, 0xa0, 0x9b, 0x90, 0x1a, 0xb1, 0x15, 0x23, 0x6c, 0xf1, 0xd6,
0x7a, 0x64, 0x2c, 0x70, 0x90, 0x98, 0x0a, 0x02, 0x88, 0x36, 0x20, 0xc9, 0xdc, 0x63, 0x94, 0x2d,
0x61, 0x2e, 0x9c, 0x8b, 0x30, 0x71, 0xa1, 0x08, 0x67, 0x1e, 0xb0, 0x3f, 0xc4, 0x01, 0x76, 0xed,
0x5e, 0xdb, 0x18, 0x12, 0x7a, 0xf4, 0x9e, 0x84, 0xf7, 0x19, 0x20, 0x93, 0x3c, 0x73, 0x3a, 0x36,
0xf9, 0xf2, 0x88, 0x98, 0x3a, 0xe9, 0x58, 0x44, 0x7f, 0x22, 0x4a, 0xe0, 0x9a, 0xe7, 0x2a, 0x5b,
0xfc, 0x84, 0xf3, 0x18, 0x15, 0x67, 0x7d, 0xe5, 0xbe, 0xd0, 0xf9, 0x69, 0xfb, 0x2f, 0xab, 0xe1,
0x9b, 0x38, 0x7b, 0x87, 0x55, 0xf4, 0x81, 0x49, 0x9f, 0x1e, 0x92, 0x6e, 0x8f, 0x0c, 0x89, 0x79,
0x21, 0xce, 0x4a, 0xb0, 0xaa, 0x45, 0x4f, 0x11, 0xec, 0x4d, 0xab, 0x27, 0xec, 0x26, 0xde, 0xc6,
0xee, 0x3b, 0xfa, 0xf3, 0xf8, 0x3a, 0x0e, 0x69, 0xd1, 0x56, 0x50, 0x09, 0x92, 0xb6, 0xa3, 0x39,
0x84, 0x91, 0xb0, 0x72, 0x0b, 0x45, 0x48, 0xd8, 0xf7, 0x2d, 0x98, 0x03, 0x50, 0x19, 0x16, 0xa8,
0xd5, 0x25, 0x96, 0x61, 0xf6, 0x58, 0xd4, 0xd3, 0xe0, 0x96, 0x6f, 0xc4, 0x63, 0x0c, 0xaa, 0xc1,
0x52, 0x78, 0x1a, 0x8b, 0xf7, 0xd8, 0x56, 0xf4, 0x3d, 0x16, 0x02, 0x08, 0xae, 0x23, 0x9b, 0x50,
0x0d, 0x56, 0x75, 0x6a, 0x9a, 0x44, 0x77, 0x0c, 0x6a, 0x76, 0xfa, 0x74, 0x64, 0xe7, 0xe4, 0x62,
0xa2, 0x94, 0xa9, 0xe6, 0x3d, 0x57, 0xd9, 0x0c, 0x9e, 0x02, 0x11, 0x80, 0x8a, 0x57, 0x26, 0x9a,
0xfb, 0x74, 0x64, 0xa3, 0x1c, 0xa4, 0x83, 0x77, 0x84, 0xcf, 0x5d, 0x06, 0x07, 0xe2, 0x5d, 0xf9,
0xf9, 0x77, 0x4a, 0x4c, 0xfd, 0x3d, 0x0e, 0x6b, 0x8d, 0x2e, 0x31, 0x1d, 0xe3, 0xb1, 0x41, 0xba,
0xff, 0x33, 0xe3, 0x3f, 0x9a, 0x2e, 0x4f, 0x46, 0x4c, 0x8a, 0x59, 0x82, 0x71, 0x72, 0x2d, 0x32,
0x4e, 0xd2, 0xcc, 0x36, 0x99, 0x1b, 0x82, 0xd1, 0xa7, 0xb0, 0x14, 0x0e, 0x60, 0x0e, 0x03, 0x4b,
0x5c, 0xfc, 0x2a, 0x01, 0x29, 0xd1, 0xf2, 0xf3, 0xb0, 0x10, 0x74, 0x19, 0x76, 0xa9, 0x8c, 0xc7,
0x32, 0xfa, 0x04, 0x16, 0x6d, 0x7a, 0x64, 0xe9, 0xa4, 0xe3, 0xdf, 0x29, 0xee, 0xd8, 0xf4, 0x5c,
0x05, 0xf1, 0x3b, 0x42, 0x46, 0x15, 0x03, 0x97, 0xf6, 0xa8, 0xe5, 0xa0, 0x4f, 0x61, 0x45, 0xd8,
0xc2, 0xff, 0x66, 0x64, 0xaa, 0x5b, 0x9e, 0xab, 0x5c, 0x8a, 0xec, 0x15, 0x76, 0x15, 0x2f, 0x73,
0x45, 0x50, 0x56, 0xf7, 0x20, 0xdb, 0x25, 0xb6, 0x63, 0x98, 0x1a, 0xcb, 0x0b, 0xbb, 0x9f, 0xff,
0x9f, 0x71, 0xc5, 0x73, 0x95, 0xcb, 0xfc, 0x8c, 0x69, 0x84, 0x8a, 0x57, 0x43, 0x2a, 0xe6, 0x49,
0x0b, 0xd6, 0xc3, 0xa8, 0xc0, 0x1d, 0x96, 0xc6, 0x6a, 0xc1, 0x73, 0x95, 0xfc, 0xf9, 0xa3, 0xc6,
0x3e, 0xa1, 0x90, 0x36, 0x70, 0x0c, 0x81, 0xdc, 0xd5, 0x1c, 0x8d, 0xbf, 0x5a, 0x31, 0x5b, 0xfb,
0xe1, 0x3a, 0x7c, 0xc4, 0x04, 0x2d, 0x2b, 0xcd, 0x5a, 0x56, 0x28, 0xdc, 0xa8, 0x5d, 0xc5, 0xcb,
0x42, 0x31, 0x6e, 0x5b, 0x6b, 0x01, 0xc2, 0xff, 0xda, 0x8e, 0x36, 0x1c, 0xe5, 0x16, 0xd8, 0x21,
0x57, 0x3d, 0x57, 0xc9, 0x45, 0x0f, 0x19, 0x43, 0x54, 0x9c, 0x15, 0xba, 0x76, 0xa0, 0x12, 0x19,
0xfe, 0x51, 0x82, 0x75, 0x9e, 0xe1, 0x8a, 0x3e, 0xa8, 0xd1, 0xe1, 0xd0, 0x70, 0x58, 0xc7, 0x9d,
0xc3, 0x9b, 0x28, 0x5c, 0x51, 0x89, 0xa9, 0x8a, 0x42, 0x20, 0xf7, 0x35, 0xbb, 0xcf, 0x27, 0x3b,
0x66, 0x6b, 0xee, 0xf0, 0xf5, 0x9f, 0x25, 0x48, 0xb2, 0x76, 0x81, 0x3e, 0x06, 0x65, 0xbf, 0x5d,
0x69, 0xd7, 0x3b, 0x0f, 0x9b, 0x8d, 0x66, 0xa3, 0xdd, 0xa8, 0x3c, 0x68, 0x3c, 0xaa, 0xef, 0x74,
0x1e, 0x36, 0xf7, 0xf7, 0xea, 0xb5, 0xc6, 0xbd, 0x46, 0x7d, 0x27, 0x1b, 0xcb, 0xaf, 0x9d, 0x9c,
0x16, 0x97, 0x23, 0x00, 0x94, 0x03, 0xe0, 0xfb, 0x7c, 0x65, 0x56, 0xca, 0x2f, 0x9c, 0x9c, 0x16,
0x65, 0x7f, 0x8d, 0x0a, 0xb0, 0xcc, 0x2d, 0x6d, 0xfc, 0x45, 0x6b, 0xaf, 0xde, 0xcc, 0xc6, 0xf3,
0x8b, 0x27, 0xa7, 0xc5, 0xb4, 0x10, 0x27, 0x3b, 0x99, 0x31, 0xc1, 0x77, 0x32, 0xcb, 0x55, 0x58,
0xe2, 0x96, 0xda, 0x83, 0xd6, 0x7e, 0x7d, 0x27, 0x2b, 0xe7, 0xe1, 0xe4, 0xb4, 0x98, 0xe2, 0x52,
0x5e, 0x7e, 0xfe, 0x7d, 0x21, 0x76, 0xfd, 0x29, 0x24, 0x59, 0xeb, 0x42, 0x1f, 0xc0, 0x66, 0x0b,
0xef, 0xd4, 0x71, 0xa7, 0xd9, 0x6a, 0xd6, 0xa7, 0xfc, 0x65, 0x47, 0xfa, 0x7a, 0xa4, 0xc2, 0x2a,
0x47, 0x3d, 0x6c, 0xb2, 0x6f, 0x7d, 0x27, 0x2b, 0xe5, 0x97, 0x4f, 0x4e, 0x8b, 0x99, 0xb1, 0xc2,
0x77, 0x98, 0x63, 0x02, 0x84, 0x70, 0x58, 0x88, 0xfc, 0xe2, 0xea, 0xee, 0x8b, 0xd7, 0x05, 0xe9,
0xe5, 0xeb, 0x82, 0xf4, 0xe7, 0xeb, 0x82, 0xf4, 0xed, 0x59, 0x21, 0xf6, 0xf2, 0xac, 0x10, 0xfb,
0xe3, 0xac, 0x10, 0x7b, 0x74, 0xfb, 0xad, 0x13, 0xef, 0xd9, 0xb6, 0x71, 0xa0, 0x6f, 0x7f, 0x74,
0xe7, 0x46, 0xf0, 0x9b, 0x0d, 0x1b, 0x81, 0x07, 0x29, 0xf6, 0x7b, 0xcc, 0xed, 0x7f, 0x02, 0x00,
0x00, 0xff, 0xff, 0xa9, 0x83, 0xf0, 0x66, 0xcf, 0x11, 0x00, 0x00,
// 1303 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x58, 0xbd, 0x6f, 0xdb, 0x46,
0x14, 0x17, 0xf5, 0x69, 0x3d, 0x7f, 0xc9, 0x67, 0xc7, 0x91, 0x95, 0x44, 0x14, 0x88, 0x0e, 0x42,
0x8a, 0xc8, 0xcd, 0x07, 0x5a, 0x20, 0x53, 0x25, 0x59, 0x41, 0x84, 0xc6, 0x92, 0x71, 0x56, 0x0a,
0x34, 0x0b, 0x41, 0x53, 0x17, 0x89, 0x90, 0xc5, 0x53, 0x49, 0x3a, 0x89, 0xf7, 0x0e, 0x81, 0x81,
0x16, 0xfd, 0x07, 0x0c, 0x14, 0xe8, 0xd4, 0xa5, 0x53, 0x81, 0x76, 0xe9, 0xd4, 0x25, 0x5b, 0x33,
0x76, 0x62, 0x8a, 0x64, 0xea, 0xca, 0xa9, 0xe8, 0x54, 0xf0, 0xee, 0x28, 0x91, 0x72, 0x9a, 0xc1,
0x0a, 0x94, 0x0c, 0x9d, 0xc8, 0xf7, 0xde, 0xef, 0xee, 0xde, 0xfb, 0xbd, 0xc7, 0xf7, 0x4e, 0x82,
0x2d, 0xe3, 0x40, 0xdf, 0xd6, 0xfb, 0x9a, 0x69, 0x92, 0xc3, 0xe0, 0x59, 0x19, 0x59, 0xd4, 0xa1,
0x68, 0xd1, 0x38, 0xd0, 0x2b, 0x42, 0x55, 0xd8, 0xe8, 0xd1, 0x1e, 0x65, 0xfa, 0x6d, 0xff, 0x8d,
0x43, 0x94, 0xbf, 0x25, 0x40, 0xbb, 0x76, 0xaf, 0xce, 0x41, 0xed, 0x11, 0x31, 0x9b, 0xa6, 0xe1,
0xa0, 0x0f, 0x21, 0x33, 0xa2, 0x96, 0xa3, 0x1a, 0xdd, 0xbc, 0x54, 0x92, 0xca, 0xd9, 0x1a, 0xf2,
0x5c, 0x79, 0xe5, 0x58, 0x1b, 0x1e, 0xde, 0x56, 0x84, 0x41, 0xc1, 0x69, 0xff, 0xad, 0xd9, 0x45,
0xb7, 0x00, 0xc4, 0x21, 0x3e, 0x3e, 0xce, 0xf0, 0x17, 0x3c, 0x57, 0x5e, 0xe3, 0xf8, 0x89, 0x4d,
0xc1, 0x59, 0x21, 0xb0, 0x55, 0x19, 0x21, 0xe4, 0x13, 0x25, 0xa9, 0xbc, 0x78, 0x63, 0xa3, 0x12,
0x72, 0xb7, 0x22, 0x3c, 0xaa, 0x25, 0x9f, 0xb9, 0x72, 0x0c, 0x07, 0x50, 0xd4, 0x84, 0xb4, 0x6d,
0xf4, 0x4c, 0x62, 0xe5, 0x93, 0x25, 0xa9, 0xbc, 0x54, 0xbb, 0xfe, 0x8f, 0x2b, 0x5f, 0xeb, 0x19,
0x4e, 0xff, 0xe8, 0xa0, 0xa2, 0xd3, 0xe1, 0xb6, 0x4e, 0xed, 0x21, 0xb5, 0xc5, 0xe3, 0x9a, 0xdd,
0x1d, 0x6c, 0x3b, 0xc7, 0x23, 0x62, 0x57, 0xaa, 0xba, 0x5e, 0xed, 0x76, 0x2d, 0x62, 0xdb, 0x58,
0x6c, 0xa0, 0xfc, 0x9a, 0x80, 0xb5, 0x68, 0xe8, 0x1d, 0xeb, 0xf8, 0xfd, 0x8d, 0x1c, 0xc3, 0x86,
0x4e, 0x8f, 0x4c, 0x87, 0x58, 0x23, 0xcd, 0x72, 0x8e, 0xd5, 0x47, 0xc4, 0xb2, 0x0d, 0x6a, 0x32,
0x1e, 0xb2, 0x35, 0xd9, 0x73, 0xe5, 0x4b, 0xe2, 0xd4, 0xd7, 0xa0, 0x14, 0xbc, 0x1e, 0x56, 0x7f,
0xce, 0xb5, 0xbe, 0xff, 0x23, 0x8b, 0xd2, 0x87, 0xaa, 0x61, 0x1a, 0x4e, 0x3e, 0xc5, 0x18, 0x0d,
0xf9, 0x3f, 0xb1, 0x29, 0x38, 0xcb, 0x04, 0x56, 0x1c, 0xb7, 0x61, 0x89, 0x5b, 0xfa, 0xc4, 0xe8,
0xf5, 0x9d, 0x7c, 0xba, 0x24, 0x95, 0x93, 0xb5, 0x8b, 0x9e, 0x2b, 0xaf, 0x87, 0xd7, 0x71, 0xab,
0x82, 0x17, 0x99, 0x78, 0x97, 0x49, 0xa1, 0xfc, 0x65, 0x66, 0xcd, 0xdf, 0x57, 0x67, 0xf2, 0x57,
0xd5, 0x07, 0xf3, 0xc8, 0xdf, 0x7f, 0x65, 0x22, 0x31, 0x43, 0x26, 0xae, 0x03, 0x27, 0x58, 0x75,
0xac, 0x63, 0x51, 0xda, 0x1b, 0x9e, 0x2b, 0xe7, 0xc2, 0x84, 0x3a, 0xd6, 0xb1, 0x82, 0x17, 0xd8,
0xbb, 0x5f, 0xa9, 0xd3, 0x69, 0x48, 0x9d, 0x2b, 0x0d, 0xe9, 0x59, 0xd3, 0xf0, 0x53, 0x1c, 0x2e,
0x44, 0xd3, 0x50, 0xa7, 0xe6, 0x43, 0xc3, 0x1a, 0xce, 0x23, 0x15, 0x63, 0xda, 0x34, 0x7d, 0xc0,
0xf8, 0x7f, 0x0d, 0x6d, 0x9a, 0x3e, 0x08, 0x68, 0xf3, 0x0b, 0x64, 0x9a, 0xb6, 0xe4, 0xb9, 0x68,
0x4b, 0xcd, 0x4a, 0xdb, 0x6f, 0x12, 0xac, 0x4f, 0x68, 0xab, 0x1f, 0x52, 0x9b, 0xcc, 0xab, 0xf3,
0x4e, 0xa2, 0x48, 0xcc, 0x1a, 0xc5, 0x2f, 0x71, 0xd8, 0x9c, 0x8a, 0x62, 0x8e, 0xd9, 0x8f, 0xb6,
0xaf, 0xc4, 0x39, 0xdb, 0xd7, 0x3b, 0x2a, 0x80, 0x17, 0x12, 0x2c, 0xef, 0xda, 0x3d, 0x4c, 0xf4,
0x47, 0x7b, 0x9a, 0x3e, 0x20, 0x0e, 0xba, 0x0e, 0xe9, 0x11, 0x7b, 0x63, 0x84, 0x2d, 0xde, 0x58,
0x8f, 0x8c, 0x05, 0x0e, 0x12, 0x53, 0x41, 0x00, 0xd1, 0x06, 0xa4, 0x98, 0x7b, 0x8c, 0xb2, 0x25,
0xcc, 0x85, 0x33, 0x11, 0x26, 0xce, 0x15, 0xe1, 0xcc, 0x03, 0xf6, 0x87, 0x38, 0xc0, 0xae, 0xdd,
0xeb, 0x18, 0x43, 0x42, 0x8f, 0xde, 0x93, 0xf0, 0x3e, 0x03, 0x64, 0x92, 0x27, 0x8e, 0x6a, 0x93,
0x2f, 0x8f, 0x88, 0xa9, 0x13, 0xd5, 0x22, 0xfa, 0x23, 0x51, 0x02, 0x57, 0x3c, 0x57, 0xde, 0xe2,
0x3b, 0x9c, 0xc5, 0x28, 0x38, 0xe7, 0x2b, 0xf7, 0x85, 0xce, 0x4f, 0xdb, 0xdb, 0xac, 0x86, 0xbf,
0xe2, 0x6c, 0x98, 0x09, 0xae, 0xda, 0x26, 0xfb, 0x94, 0xde, 0x1e, 0x65, 0x9f, 0x00, 0x67, 0x41,
0xd5, 0xfd, 0x7d, 0xc5, 0xa7, 0xb2, 0xe9, 0xb9, 0x32, 0x0a, 0x33, 0xc6, 0x8c, 0x0a, 0xe6, 0x1f,
0x15, 0xf7, 0x60, 0x96, 0x8f, 0xe5, 0xf5, 0x5c, 0xa7, 0x66, 0xe5, 0x7a, 0xe6, 0x89, 0xf5, 0x4d,
0x9c, 0xdd, 0x79, 0xab, 0xfa, 0xc0, 0xa4, 0x8f, 0x0f, 0x49, 0xb7, 0x47, 0x86, 0xc4, 0x3c, 0x57,
0x7d, 0x96, 0x61, 0x55, 0x8b, 0xee, 0x22, 0x68, 0x9f, 0x56, 0x4f, 0xd2, 0x92, 0x78, 0x53, 0x25,
0xbf, 0xa3, 0x56, 0xf4, 0x75, 0x1c, 0x32, 0xa2, 0x85, 0xa3, 0x32, 0xa4, 0x6c, 0x47, 0x73, 0x08,
0x23, 0x61, 0xe5, 0x06, 0x8a, 0x90, 0xb0, 0xef, 0x5b, 0x30, 0x07, 0xa0, 0x0a, 0x2c, 0x50, 0xab,
0x4b, 0x2c, 0xc3, 0xec, 0xb1, 0xa8, 0xa7, 0xc1, 0x6d, 0xdf, 0x88, 0xc7, 0x18, 0x54, 0x87, 0xa5,
0xf0, 0xcd, 0x47, 0xdc, 0x7d, 0xb7, 0xa2, 0x77, 0xdf, 0x10, 0x40, 0x70, 0x1d, 0x59, 0x84, 0xea,
0xb0, 0xaa, 0x53, 0xd3, 0x24, 0xba, 0x63, 0x50, 0x53, 0xed, 0xd3, 0x91, 0x9d, 0x4f, 0x96, 0x12,
0xe5, 0x6c, 0xad, 0xe0, 0xb9, 0xf2, 0x66, 0x70, 0xed, 0x8a, 0x00, 0x14, 0xbc, 0x32, 0xd1, 0xdc,
0xa5, 0x23, 0x1b, 0xe5, 0x21, 0x13, 0xdc, 0xd9, 0x7c, 0xee, 0xb2, 0x38, 0x10, 0x6f, 0x27, 0x9f,
0x7e, 0x27, 0xc7, 0x94, 0xdf, 0xe3, 0xb0, 0xd6, 0xec, 0x12, 0xd3, 0x31, 0x1e, 0x1a, 0xa4, 0xfb,
0x3f, 0x33, 0xfe, 0x05, 0xf5, 0xe2, 0x64, 0x9c, 0xa7, 0x99, 0x25, 0x18, 0xdd, 0x57, 0x22, 0xa3,
0x3b, 0xc3, 0x6c, 0x93, 0x19, 0x2d, 0x18, 0x7d, 0x0c, 0x4b, 0xe1, 0x00, 0xe6, 0x70, 0x39, 0x10,
0x07, 0xbf, 0x48, 0x40, 0x5a, 0x8c, 0xd7, 0x02, 0x2c, 0x04, 0x5d, 0x86, 0x1d, 0x9a, 0xc4, 0x63,
0xd9, 0xef, 0x8f, 0x36, 0x3d, 0xb2, 0x74, 0xa2, 0xfa, 0x67, 0x8a, 0x33, 0x42, 0xfd, 0x31, 0x64,
0x54, 0x30, 0x70, 0x69, 0x8f, 0x5a, 0x0e, 0xfa, 0x14, 0x56, 0x84, 0x2d, 0xfc, 0x93, 0x2e, 0x5b,
0xdb, 0xf2, 0x5c, 0xf9, 0x42, 0x64, 0xad, 0xb0, 0x2b, 0x78, 0x99, 0x2b, 0x82, 0xb2, 0xba, 0x03,
0xb9, 0x2e, 0xb1, 0x1d, 0xc3, 0xd4, 0x58, 0x5e, 0xd8, 0xf9, 0xfc, 0x37, 0xdd, 0x25, 0xcf, 0x95,
0x2f, 0xf2, 0x3d, 0xa6, 0x11, 0x0a, 0x5e, 0x0d, 0xa9, 0x98, 0x27, 0x6d, 0x58, 0x0f, 0xa3, 0x02,
0x77, 0x58, 0x1a, 0x6b, 0x45, 0xcf, 0x95, 0x0b, 0x67, 0xb7, 0x1a, 0xfb, 0x84, 0x42, 0xda, 0xc0,
0x31, 0x04, 0xc9, 0xae, 0xe6, 0x68, 0xbc, 0xdf, 0x62, 0xf6, 0xee, 0x87, 0xeb, 0xf0, 0x11, 0x15,
0xb4, 0xac, 0x0c, 0x6b, 0x59, 0xa1, 0x70, 0xa3, 0x76, 0x05, 0x2f, 0x0b, 0xc5, 0xb8, 0x6d, 0xad,
0x05, 0x08, 0xff, 0x69, 0x3b, 0xda, 0x70, 0x94, 0x5f, 0x60, 0x9b, 0x5c, 0xf6, 0x5c, 0x39, 0x1f,
0xdd, 0x64, 0x0c, 0x51, 0x70, 0x4e, 0xe8, 0x3a, 0x81, 0x4a, 0x64, 0xf8, 0x47, 0x09, 0xd6, 0x79,
0x86, 0xab, 0xfa, 0xa0, 0x4e, 0x87, 0x43, 0xc3, 0x61, 0x1d, 0x77, 0x0e, 0xf7, 0xcf, 0x70, 0x45,
0x25, 0xa6, 0x2a, 0x0a, 0x41, 0xb2, 0xaf, 0xd9, 0x7d, 0x7e, 0x8b, 0xc2, 0xec, 0x9d, 0x3b, 0x7c,
0xf5, 0x67, 0x09, 0x52, 0xac, 0x5d, 0xa0, 0x8f, 0x41, 0xde, 0xef, 0x54, 0x3b, 0x0d, 0xf5, 0x7e,
0xab, 0xd9, 0x6a, 0x76, 0x9a, 0xd5, 0x7b, 0xcd, 0x07, 0x8d, 0x1d, 0xf5, 0x7e, 0x6b, 0x7f, 0xaf,
0x51, 0x6f, 0xde, 0x69, 0x36, 0x76, 0x72, 0xb1, 0xc2, 0xda, 0xc9, 0x69, 0x69, 0x39, 0x02, 0x40,
0x79, 0x00, 0xbe, 0xce, 0x57, 0xe6, 0xa4, 0xc2, 0xc2, 0xc9, 0x69, 0x29, 0xe9, 0xbf, 0xa3, 0x22,
0x2c, 0x73, 0x4b, 0x07, 0x7f, 0xd1, 0xde, 0x6b, 0xb4, 0x72, 0xf1, 0xc2, 0xe2, 0xc9, 0x69, 0x29,
0x23, 0xc4, 0xc9, 0x4a, 0x66, 0x4c, 0xf0, 0x95, 0xcc, 0x72, 0x19, 0x96, 0xb8, 0xa5, 0x7e, 0xaf,
0xbd, 0xdf, 0xd8, 0xc9, 0x25, 0x0b, 0x70, 0x72, 0x5a, 0x4a, 0x73, 0xa9, 0x90, 0x7c, 0xfa, 0x7d,
0x31, 0x76, 0xf5, 0x31, 0xa4, 0x58, 0xeb, 0x42, 0x1f, 0xc0, 0x66, 0x1b, 0xef, 0x34, 0xb0, 0xda,
0x6a, 0xb7, 0x1a, 0x53, 0xfe, 0xb2, 0x2d, 0x7d, 0x3d, 0x52, 0x60, 0x95, 0xa3, 0xee, 0xb7, 0xd8,
0xb3, 0xb1, 0x93, 0x93, 0x0a, 0xcb, 0x27, 0xa7, 0xa5, 0xec, 0x58, 0xe1, 0x3b, 0xcc, 0x31, 0x01,
0x42, 0x38, 0x2c, 0x44, 0x7e, 0x70, 0x6d, 0xf7, 0xd9, 0xcb, 0xa2, 0xf4, 0xfc, 0x65, 0x51, 0xfa,
0xf3, 0x65, 0x51, 0xfa, 0xf6, 0x55, 0x31, 0xf6, 0xfc, 0x55, 0x31, 0xf6, 0xc7, 0xab, 0x62, 0xec,
0xc1, 0xcd, 0x37, 0x4e, 0xbc, 0x27, 0xdb, 0xc6, 0x81, 0xbe, 0xfd, 0xd1, 0xad, 0x6b, 0xc1, 0xff,
0x63, 0x6c, 0x04, 0x1e, 0xa4, 0xd9, 0x7f, 0x5f, 0x37, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xbc,
0xba, 0x59, 0x74, 0x3b, 0x13, 0x00, 0x00,
}
func (m *MsgChannelOpenInit) Marshal() (dAtA []byte, err error) {
@@ -1593,6 +1684,70 @@ func (m *MsgTimeout) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
func (m *MsgTimeoutOnClose) 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 *MsgTimeoutOnClose) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MsgTimeoutOnClose) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Signer) > 0 {
i -= len(m.Signer)
copy(dAtA[i:], m.Signer)
i = encodeVarintChannel(dAtA, i, uint64(len(m.Signer)))
i--
dAtA[i] = 0x32
}
if m.NextSequenceRecv != 0 {
i = encodeVarintChannel(dAtA, i, uint64(m.NextSequenceRecv))
i--
dAtA[i] = 0x28
}
if m.ProofHeight != 0 {
i = encodeVarintChannel(dAtA, i, uint64(m.ProofHeight))
i--
dAtA[i] = 0x20
}
if len(m.ProofClose) > 0 {
i -= len(m.ProofClose)
copy(dAtA[i:], m.ProofClose)
i = encodeVarintChannel(dAtA, i, uint64(len(m.ProofClose)))
i--
dAtA[i] = 0x1a
}
if len(m.Proof) > 0 {
i -= len(m.Proof)
copy(dAtA[i:], m.Proof)
i = encodeVarintChannel(dAtA, i, uint64(len(m.Proof)))
i--
dAtA[i] = 0x12
}
{
size, err := m.Packet.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintChannel(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
return len(dAtA) - i, nil
}
func (m *MsgAcknowledgement) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@@ -2167,6 +2322,35 @@ func (m *MsgTimeout) Size() (n int) {
return n
}
func (m *MsgTimeoutOnClose) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = m.Packet.Size()
n += 1 + l + sovChannel(uint64(l))
l = len(m.Proof)
if l > 0 {
n += 1 + l + sovChannel(uint64(l))
}
l = len(m.ProofClose)
if l > 0 {
n += 1 + l + sovChannel(uint64(l))
}
if m.ProofHeight != 0 {
n += 1 + sovChannel(uint64(m.ProofHeight))
}
if m.NextSequenceRecv != 0 {
n += 1 + sovChannel(uint64(m.NextSequenceRecv))
}
l = len(m.Signer)
if l > 0 {
n += 1 + l + sovChannel(uint64(l))
}
return n
}
func (m *MsgAcknowledgement) Size() (n int) {
if m == nil {
return 0
@@ -3953,6 +4137,232 @@ func (m *MsgTimeout) Unmarshal(dAtA []byte) error {
}
return nil
}
func (m *MsgTimeoutOnClose) 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 ErrIntOverflowChannel
}
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: MsgTimeoutOnClose: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MsgTimeoutOnClose: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Packet", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowChannel
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthChannel
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthChannel
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := m.Packet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowChannel
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthChannel
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthChannel
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Proof = append(m.Proof[:0], dAtA[iNdEx:postIndex]...)
if m.Proof == nil {
m.Proof = []byte{}
}
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ProofClose", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowChannel
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthChannel
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthChannel
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ProofClose = append(m.ProofClose[:0], dAtA[iNdEx:postIndex]...)
if m.ProofClose == nil {
m.ProofClose = []byte{}
}
iNdEx = postIndex
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", wireType)
}
m.ProofHeight = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowChannel
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ProofHeight |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 5:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field NextSequenceRecv", wireType)
}
m.NextSequenceRecv = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowChannel
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.NextSequenceRecv |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 6:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowChannel
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthChannel
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthChannel
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Signer = append(m.Signer[:0], dAtA[iNdEx:postIndex]...)
if m.Signer == nil {
m.Signer = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipChannel(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthChannel
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthChannel
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *MsgAcknowledgement) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
+1
View File
@@ -20,6 +20,7 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
&MsgRecvPacket{},
&MsgAcknowledgement{},
&MsgTimeout{},
&MsgTimeoutOnClose{},
)
}
+54
View File
@@ -433,6 +433,60 @@ func (msg MsgTimeout) Type() string {
return "timeout_packet"
}
// NewMsgTimeoutOnClose constructs new MsgTimeoutOnClose
func NewMsgTimeoutOnClose(
packet Packet, nextSequenceRecv uint64,
proof, proofClose []byte,
proofHeight uint64, signer sdk.AccAddress,
) *MsgTimeoutOnClose {
return &MsgTimeoutOnClose{
Packet: packet,
NextSequenceRecv: nextSequenceRecv,
Proof: proof,
ProofClose: proofClose,
ProofHeight: proofHeight,
Signer: signer,
}
}
// Route implements sdk.Msg
func (msg MsgTimeoutOnClose) Route() string {
return host.RouterKey
}
// ValidateBasic implements sdk.Msg
func (msg MsgTimeoutOnClose) ValidateBasic() error {
if len(msg.Proof) == 0 {
return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof")
}
if len(msg.ProofClose) == 0 {
return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof of closed counterparty channel end")
}
if msg.ProofHeight == 0 {
return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be > 0")
}
if msg.Signer.Empty() {
return sdkerrors.ErrInvalidAddress
}
return msg.Packet.ValidateBasic()
}
// GetSignBytes implements sdk.Msg
func (msg MsgTimeoutOnClose) GetSignBytes() []byte {
return sdk.MustSortJSON(SubModuleCdc.MustMarshalJSON(&msg))
}
// GetSigners implements sdk.Msg
func (msg MsgTimeoutOnClose) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Signer}
}
// Type implements sdk.Msg
func (msg MsgTimeoutOnClose) Type() string {
return "timeout_on_close_packet"
}
var _ sdk.Msg = &MsgAcknowledgement{}
// NewMsgAcknowledgement constructs a new MsgAcknowledgement
+34 -4
View File
@@ -43,7 +43,7 @@ var (
unknownPacketData = []byte("unknown")
packet = types.NewPacket(validPacketData, 1, portid, chanid, cpportid, cpchanid, timeoutHeight, timeoutTimestamp)
unknownPacket = types.NewPacket(unknownPacketData, 0, portid, chanid, cpportid, cpchanid, timeoutHeight, timeoutTimestamp)
invalidPacket = types.NewPacket(unknownPacketData, 0, portid, chanid, cpportid, cpchanid, timeoutHeight, timeoutTimestamp)
emptyProof = []byte{}
invalidProofs1 = commitmentexported.Proof(nil)
@@ -383,7 +383,7 @@ func (suite *MsgTestSuite) TestMsgRecvPacketValidation() {
types.NewMsgRecvPacket(packet, suite.proof, 0, addr1), // proof height is zero
types.NewMsgRecvPacket(packet, emptyProof, 1, addr1), // empty proof
types.NewMsgRecvPacket(packet, suite.proof, 1, emptyAddr), // missing signer address
types.NewMsgRecvPacket(unknownPacket, suite.proof, 1, addr1), // unknown packet
types.NewMsgRecvPacket(invalidPacket, suite.proof, 1, addr1), // unknown packet
}
testCases := []struct {
@@ -436,7 +436,7 @@ func (suite *MsgTestSuite) TestMsgTimeout() {
types.NewMsgTimeout(packet, 1, suite.proof, 0, addr),
types.NewMsgTimeout(packet, 1, suite.proof, 1, emptyAddr),
types.NewMsgTimeout(packet, 1, emptyProof, 1, addr),
types.NewMsgTimeout(unknownPacket, 1, suite.proof, 1, addr),
types.NewMsgTimeout(invalidPacket, 1, suite.proof, 1, addr),
}
testCases := []struct {
@@ -461,6 +461,36 @@ func (suite *MsgTestSuite) TestMsgTimeout() {
}
}
// TestMsgTimeoutOnClose tests ValidateBasic for MsgTimeoutOnClose
func (suite *MsgTestSuite) TestMsgTimeoutOnClose() {
testCases := []struct {
name string
msg sdk.Msg
expPass bool
}{
{"success", types.NewMsgTimeoutOnClose(packet, 1, suite.proof, suite.proof, 1, addr), true},
{"empty proof", types.NewMsgTimeoutOnClose(packet, 1, emptyProof, suite.proof, 1, addr), false},
{"empty proof close", types.NewMsgTimeoutOnClose(packet, 1, suite.proof, emptyProof, 1, addr), false},
{"proof height is zero", types.NewMsgTimeoutOnClose(packet, 1, suite.proof, suite.proof, 0, addr), false},
{"signer address is empty", types.NewMsgTimeoutOnClose(packet, 1, suite.proof, suite.proof, 1, emptyAddr), false},
{"invalid packet", types.NewMsgTimeoutOnClose(invalidPacket, 1, suite.proof, suite.proof, 1, addr), false},
}
for _, tc := range testCases {
tc := tc
suite.Run(tc.name, func() {
err := tc.msg.ValidateBasic()
if tc.expPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
}
}
// TestMsgAcknowledgement tests ValidateBasic for MsgAcknowledgement
func (suite *MsgTestSuite) TestMsgAcknowledgement() {
testMsgs := []*types.MsgAcknowledgement{
@@ -468,7 +498,7 @@ func (suite *MsgTestSuite) TestMsgAcknowledgement() {
types.NewMsgAcknowledgement(packet, packet.GetData(), suite.proof, 0, addr),
types.NewMsgAcknowledgement(packet, packet.GetData(), suite.proof, 1, emptyAddr),
types.NewMsgAcknowledgement(packet, packet.GetData(), emptyProof, 1, addr),
types.NewMsgAcknowledgement(unknownPacket, packet.GetData(), suite.proof, 1, addr),
types.NewMsgAcknowledgement(invalidPacket, packet.GetData(), suite.proof, 1, addr),
}
testCases := []struct {
+33
View File
@@ -260,6 +260,39 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
return res, nil
case *channeltypes.MsgTimeoutOnClose:
// Lookup module by channel capability
module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.Packet.SourcePort, msg.Packet.SourceChannel)
if err != nil {
return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id")
}
// Retrieve callbacks from router
cbs, ok := k.Router.GetRoute(module)
if !ok {
return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module)
}
// Perform TAO verification
if err := k.ChannelKeeper.TimeoutOnClose(ctx, cap, msg.Packet, msg.Proof, msg.ProofClose, msg.ProofHeight, msg.NextSequenceRecv); err != nil {
return nil, sdkerrors.Wrap(err, "timeout on close packet verification failed")
}
// Perform application logic callback
// NOTE: MsgTimeout and MsgTimeoutOnClose use the same "OnTimeoutPacket"
// application logic callback.
res, err := cbs.OnTimeoutPacket(ctx, msg.Packet)
if err != nil {
return nil, sdkerrors.Wrap(err, "timeout packet callback failed")
}
// Delete packet commitment
if err = k.ChannelKeeper.TimeoutExecuted(ctx, cap, msg.Packet); err != nil {
return nil, err
}
return res, nil
default:
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized IBC message type: %T", msg)
}
+229 -19
View File
@@ -5,6 +5,7 @@ import (
"github.com/stretchr/testify/suite"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/ibc"
ibctransfertypes "github.com/cosmos/cosmos-sdk/x/ibc-transfer/types"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
@@ -13,8 +14,9 @@ import (
ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing"
)
var (
const (
timeoutHeight = uint64(10000)
totalPackets = uint64(10)
)
type HandlerTestSuite struct {
@@ -329,7 +331,7 @@ func (suite *HandlerTestSuite) TestHandleTimeoutPacket() {
// send from chainA to chainB
msg := ibctransfertypes.NewMsgTransfer(channelA.PortID, channelA.ID, ibctesting.TestCoin, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), packet.GetTimeoutHeight(), packet.GetTimeoutTimestamp())
err := suite.coordinator.SendMsgs(suite.chainA, suite.chainB, clientB, msg)
err := suite.coordinator.SendMsg(suite.chainA, suite.chainB, clientB, msg)
suite.Require().NoError(err) // message committed
// need to update chainA client to prove missing ack
@@ -343,7 +345,7 @@ func (suite *HandlerTestSuite) TestHandleTimeoutPacket() {
// send from chainA to chainB
msg := ibctransfertypes.NewMsgTransfer(channelA.PortID, channelA.ID, ibctesting.TestCoin, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), packet.GetTimeoutHeight(), packet.GetTimeoutTimestamp())
err := suite.coordinator.SendMsgs(suite.chainA, suite.chainB, clientB, msg)
err := suite.coordinator.SendMsg(suite.chainA, suite.chainB, clientB, msg)
suite.Require().NoError(err) // message committed
// need to update chainA client to prove missing ack
@@ -355,15 +357,18 @@ func (suite *HandlerTestSuite) TestHandleTimeoutPacket() {
// setup uses an UNORDERED channel
clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
// attempts to timeout packet with sequence 10 without timing out packet with sequence 1
for i := uint64(1); i < 10; i++ {
packet = channeltypes.NewPacket(suite.chainA.GetPacketData(suite.chainB), i, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), 0)
// send from chainA to chainB
msg := ibctransfertypes.NewMsgTransfer(channelA.PortID, channelA.ID, ibctesting.TestCoin, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), packet.GetTimeoutHeight(), packet.GetTimeoutTimestamp())
err := suite.coordinator.SendMsgs(suite.chainA, suite.chainB, clientB, msg)
suite.Require().NoError(err) // message committed
msgs := make([]sdk.Msg, totalPackets)
// attempts to timeout the last packet sent without timing out the first packet
// packet sequences begin at 1
for i := uint64(0); i < totalPackets; i++ {
packet = channeltypes.NewPacket(suite.chainA.GetPacketData(suite.chainB), i+1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), 0)
msgs[i] = ibctransfertypes.NewMsgTransfer(channelA.PortID, channelA.ID, ibctesting.TestCoin, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), packet.GetTimeoutHeight(), packet.GetTimeoutTimestamp())
}
// send from chainA to chainB
err := suite.coordinator.SendMsgs(suite.chainA, suite.chainB, clientB, msgs)
suite.Require().NoError(err) // message committed
// need to update chainA client to prove missing ack
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
packetKey = host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
@@ -373,15 +378,18 @@ func (suite *HandlerTestSuite) TestHandleTimeoutPacket() {
clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, channeltypes.ORDERED)
// attempts to timeout packet with sequence 10 without timing out packet with sequence 1
for i := uint64(1); i < 10; i++ {
packet = channeltypes.NewPacket(suite.chainA.GetPacketData(suite.chainB), i, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), 0)
// send from chainA to chainB
msg := ibctransfertypes.NewMsgTransfer(channelA.PortID, channelA.ID, ibctesting.TestCoin, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), packet.GetTimeoutHeight(), packet.GetTimeoutTimestamp())
err := suite.coordinator.SendMsgs(suite.chainA, suite.chainB, clientB, msg)
suite.Require().NoError(err) // message committed
msgs := make([]sdk.Msg, totalPackets)
// attempts to timeout the last packet sent without timing out the first packet
// packet sequences begin at 1
for i := uint64(0); i < totalPackets; i++ {
packet = channeltypes.NewPacket(suite.chainA.GetPacketData(suite.chainB), i+1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), 0)
msgs[i] = ibctransfertypes.NewMsgTransfer(channelA.PortID, channelA.ID, ibctesting.TestCoin, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), packet.GetTimeoutHeight(), packet.GetTimeoutTimestamp())
}
// send from chainA to chainB
err := suite.coordinator.SendMsgs(suite.chainA, suite.chainB, clientB, msgs)
suite.Require().NoError(err) // message committed
// need to update chainA client to prove missing ack
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
packetKey = host.KeyNextSequenceRecv(packet.GetDestPort(), packet.GetDestChannel())
@@ -433,3 +441,205 @@ func (suite *HandlerTestSuite) TestHandleTimeoutPacket() {
})
}
}
// tests the IBC handler timing out a packet via channel closure on ordered
// and unordered channels. It verifies that the deletion of a packet
// commitment occurs. It tests high level properties like ordering and basic
// sanity checks. More rigorous testing of 'TimeoutOnClose' and
//'TimeoutExecuted' can be found in the 04-channel/keeper/timeout_test.go.
func (suite *HandlerTestSuite) TestHandleTimeoutOnClosePacket() {
var (
packet channeltypes.Packet
packetKey []byte
counterpartyChannel ibctesting.TestChannel
)
testCases := []struct {
name string
malleate func()
expPass bool
}{
{"success: ORDERED", func() {
clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, channeltypes.ORDERED)
packet = channeltypes.NewPacket(suite.chainA.GetPacketData(suite.chainB), 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0)
counterpartyChannel = ibctesting.TestChannel{
PortID: channelB.PortID,
ID: channelB.ID,
CounterpartyClientID: clientA,
}
// send from chainA to chainB
msg := ibctransfertypes.NewMsgTransfer(channelA.PortID, channelA.ID, ibctesting.TestCoin, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), packet.GetTimeoutHeight(), packet.GetTimeoutTimestamp())
err := suite.coordinator.SendMsg(suite.chainA, suite.chainB, clientB, msg)
suite.Require().NoError(err) // message committed
// need to update chainA client to prove missing ack
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
packetKey = host.KeyNextSequenceRecv(packet.GetDestPort(), packet.GetDestChannel())
// close counterparty channel
suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, counterpartyChannel)
}, true},
{"success: UNORDERED", func() {
clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
packet = channeltypes.NewPacket(suite.chainA.GetPacketData(suite.chainB), 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0)
counterpartyChannel = ibctesting.TestChannel{
PortID: channelB.PortID,
ID: channelB.ID,
CounterpartyClientID: clientA,
}
// send from chainA to chainB
msg := ibctransfertypes.NewMsgTransfer(channelA.PortID, channelA.ID, ibctesting.TestCoin, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), packet.GetTimeoutHeight(), packet.GetTimeoutTimestamp())
err := suite.coordinator.SendMsg(suite.chainA, suite.chainB, clientB, msg)
suite.Require().NoError(err) // message committed
// need to update chainA client to prove missing ack
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
packetKey = host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
// close counterparty channel
suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, counterpartyChannel)
}, true},
{"success: UNORDERED timeout out of order packet", func() {
// setup uses an UNORDERED channel
clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
counterpartyChannel = ibctesting.TestChannel{
PortID: channelB.PortID,
ID: channelB.ID,
CounterpartyClientID: clientA,
}
msgs := make([]sdk.Msg, totalPackets)
// attempts to timeout the last packet sent without timing out the first packet
// packet sequences begin at 1
for i := uint64(0); i < totalPackets; i++ {
packet = channeltypes.NewPacket(suite.chainA.GetPacketData(suite.chainB), i+1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0)
msgs[i] = ibctransfertypes.NewMsgTransfer(channelA.PortID, channelA.ID, ibctesting.TestCoin, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), packet.GetTimeoutHeight(), packet.GetTimeoutTimestamp())
}
// send from chainA to chainB
err := suite.coordinator.SendMsgs(suite.chainA, suite.chainB, clientB, msgs)
suite.Require().NoError(err) // message committed
// need to update chainA client to prove missing ack
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
packetKey = host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
// close counterparty channel
suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, counterpartyChannel)
}, true},
{"success: ORDERED timeout out of order packet", func() {
clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, channeltypes.ORDERED)
counterpartyChannel = ibctesting.TestChannel{
PortID: channelB.PortID,
ID: channelB.ID,
CounterpartyClientID: clientA,
}
msgs := make([]sdk.Msg, totalPackets)
// attempts to timeout the last packet sent without timing out the first packet
// packet sequences begin at 1
for i := uint64(0); i < totalPackets; i++ {
packet = channeltypes.NewPacket(suite.chainA.GetPacketData(suite.chainB), i+1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0)
msgs[i] = ibctransfertypes.NewMsgTransfer(channelA.PortID, channelA.ID, ibctesting.TestCoin, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), packet.GetTimeoutHeight(), packet.GetTimeoutTimestamp())
}
// send from chainA to chainB
err := suite.coordinator.SendMsgs(suite.chainA, suite.chainB, clientB, msgs)
suite.Require().NoError(err) // message committed
// need to update chainA client to prove missing ack
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
packetKey = host.KeyNextSequenceRecv(packet.GetDestPort(), packet.GetDestChannel())
// close counterparty channel
suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, counterpartyChannel)
}, true},
{"channel does not exist", func() {
// any non-nil value of packet is valid
suite.Require().NotNil(packet)
packetKey = host.KeyNextSequenceRecv(packet.GetDestPort(), packet.GetDestChannel())
}, false},
{"UNORDERED: packet not sent", func() {
clientA, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
packet = channeltypes.NewPacket(suite.chainA.GetPacketData(suite.chainB), 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0)
packetKey = host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
counterpartyChannel = ibctesting.TestChannel{
PortID: channelB.PortID,
ID: channelB.ID,
CounterpartyClientID: clientA,
}
// close counterparty channel
suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, counterpartyChannel)
}, false},
{"ORDERED: channel not closed", func() {
clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, channeltypes.ORDERED)
packet = channeltypes.NewPacket(suite.chainA.GetPacketData(suite.chainB), 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0)
counterpartyChannel = ibctesting.TestChannel{
PortID: channelB.PortID,
ID: channelB.ID,
CounterpartyClientID: clientA,
}
// send from chainA to chainB
msg := ibctransfertypes.NewMsgTransfer(channelA.PortID, channelA.ID, ibctesting.TestCoin, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), packet.GetTimeoutHeight(), packet.GetTimeoutTimestamp())
err := suite.coordinator.SendMsg(suite.chainA, suite.chainB, clientB, msg)
suite.Require().NoError(err) // message committed
// need to update chainA client to prove missing ack
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
packetKey = host.KeyNextSequenceRecv(packet.GetDestPort(), packet.GetDestChannel())
}, false},
}
for _, tc := range testCases {
tc := tc
suite.Run(tc.name, func() {
suite.SetupTest() // reset
handler := ibc.NewHandler(*suite.chainA.App.IBCKeeper)
tc.malleate()
proof, proofHeight := suite.chainB.QueryProof(packetKey)
channelKey := host.KeyChannel(counterpartyChannel.PortID, counterpartyChannel.ID)
proofClosed, _ := suite.chainB.QueryProof(channelKey)
msg := channeltypes.NewMsgTimeoutOnClose(packet, 1, proof, proofClosed, proofHeight, suite.chainA.SenderAccount.GetAddress())
_, err := handler(suite.chainA.GetContext(), msg)
if tc.expPass {
suite.Require().NoError(err)
// replay should return an error
_, err := handler(suite.chainA.GetContext(), msg)
suite.Require().Error(err)
// verify packet commitment was deleted
has := suite.chainA.App.IBCKeeper.ChannelKeeper.HasPacketCommitment(suite.chainA.GetContext(), packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
suite.Require().False(has)
} else {
suite.Require().Error(err)
}
})
}
}
+99
View File
@@ -324,3 +324,102 @@ This message is expected to fail if:
- `ProofInit` does not prove that the counterparty set its channel to state CLOSED
The message closes a channel on chain B for the given Port ID and Channel ID.
### MsgRecvPacket
A packet is received on chain B using the `MsgRecvPacket`.
```go
type MsgRecvPacket struct {
Packet Packet
Proof []byte
ProofHeight uint64
Signer sdk.AccAddress
}
```
This message is expected to fail if:
- `Proof` is empty
- `ProofHeight` is zero
- `Signer` is empty
- `Packet` fails basic validation
- `Proof` does not prove that the counterparty sent the `Packet`.
The message receives a packet on chain B.
### MsgTimeout
A packet is timed out on chain A using the `MsgTimeout`.
```go
type MsgTimeout struct {
Packet Packet
Proof []byte
ProofHeight uint64
NextSequenceRecv uint64
Signer sdk.AccAddress
}
```
This message is expected to fail if:
- `Proof` is empty
- `ProofHeight` is zero
- `NextSequenceRecv` is zero
- `Signer` is empty
- `Packet` fails basic validation
- `Proof` does not prove that the packet has not been received on the counterparty chain.
The message times out a packet on chain B.
### MsgTimeoutOnClose
A packet is timed out on chain A due to the closure of the channel end on chain B using
the `MsgTimeoutOnClose`.
```go
type MsgTimeoutOnClose struct {
Packet Packet
Proof []byte
ProofClose []byte
ProofHeight uint64
NextSequenceRecv uint64
Signer sdk.AccAddress
}
```
This message is expected to fail if:
- `Proof` is empty
- `ProofClose` is empty
- `ProofHeight` is zero
- `NextSequenceRecv` is zero
- `Signer` is empty
- `Packet` fails basic validation
- `Proof` does not prove that the packet has not been received on the counterparty chain.
- `ProofClose` does not prove that the counterparty channel end has been closed.
The message times out a packet on chain B.
### MsgAcknowledgement
A packet is acknowledged on chain A using the `MsgAcknowledgement`.
```go
type MsgAcknowledgement struct {
Packet Packet
Acknowledgement []byte
Proof []byte
ProofHeight uint64
Signer sdk.AccAddress
}
```
This message is expected to fail if:
- `Proof` is empty
- `ProofHeight` is zero
- `Signer` is empty
- `Packet` fails basic validation
- `Acknowledgement` is empty
- `Proof` does not prove that the counterparty received the `Packet`.
The message receives a packet on chain A.
+3 -3
View File
@@ -183,7 +183,7 @@ callbacks to IBC applications.
| message | action | application-module-defined-field |
| message | module | ibc-channel |
### RecvPacket
### MsgRecvPacket
| Type | Attribute Key | Attribute Value |
|-------------|--------------------------|----------------------|
@@ -200,7 +200,7 @@ callbacks to IBC applications.
| message | action | recv_packet |
| message | module | ibc-channel |
### AcknowledgePacket
### MsgAcknowledgePacket
| Type | Attribute Key | Attribute Value |
|--------------------|--------------------------|----------------------|
@@ -215,7 +215,7 @@ callbacks to IBC applications.
| message | action | acknowledge_packet |
| message | module | ibc-channel |
### TimeoutPacket & TimeoutOnClose
### MsgTimeoutPacket & MsgTimeoutOnClose
| Type | Attribute Key | Attribute Value |
|----------------|--------------------------|----------------------|
+9 -3
View File
@@ -259,7 +259,7 @@ func (coord *Coordinator) RecvPacket(
recvMsg := channeltypes.NewMsgRecvPacket(packet, proof, proofHeight, counterparty.SenderAccount.GetAddress())
// receive on counterparty and update source client
return coord.SendMsgs(counterparty, source, sourceClient, recvMsg)
return coord.SendMsgs(counterparty, source, sourceClient, []sdk.Msg{recvMsg})
}
// AcknowledgePacket acknowledges on the source chain the packet received on
@@ -277,7 +277,7 @@ func (coord *Coordinator) AcknowledgePacket(
proof, proofHeight := counterparty.QueryProof(packetKey)
ackMsg := channeltypes.NewMsgAcknowledgement(packet, ack, proof, proofHeight, source.SenderAccount.GetAddress())
return coord.SendMsgs(source, counterparty, counterpartyClient, ackMsg)
return coord.SendMsgs(source, counterparty, counterpartyClient, []sdk.Msg{ackMsg})
}
// RelayPacket receives a channel packet on counterparty, queries the ack
@@ -305,9 +305,15 @@ func (coord *Coordinator) IncrementTime() {
}
}
// SendMsg delivers a single provided message to the chain. The counterparty
// client is update with the new source consensus state.
func (coord *Coordinator) SendMsg(source, counterparty *TestChain, counterpartyClientID string, msg sdk.Msg) error {
return coord.SendMsgs(source, counterparty, counterpartyClientID, []sdk.Msg{msg})
}
// SendMsgs delivers the provided messages to the chain. The counterparty
// client is updated with the new source consensus state.
func (coord *Coordinator) SendMsgs(source, counterparty *TestChain, counterpartyClientID string, msgs ...sdk.Msg) error {
func (coord *Coordinator) SendMsgs(source, counterparty *TestChain, counterpartyClientID string, msgs []sdk.Msg) error {
if err := source.SendMsgs(msgs...); err != nil {
return err
}