1use crate::ibc_types::{IbcChannelId, IbcConnectionId, IbcPortId};
2use crate::prelude::*;
3
4use crate::events::{
5 EVENT_ATTR_IBC_CONNECTION_ID, EVENT_ATTR_IBC_PACKET_ACK_HEX, EVENT_ATTR_IBC_PACKET_DATA_HEX,
6 EVENT_ATTR_IBC_PACKET_DST_CHANNEL, EVENT_ATTR_IBC_PACKET_DST_PORT,
7 EVENT_ATTR_IBC_PACKET_SEQUENCE, EVENT_ATTR_IBC_PACKET_SRC_CHANNEL,
8 EVENT_ATTR_IBC_PACKET_SRC_PORT, EVENT_ATTR_IBC_PACKET_TIMEOUT_HEIGHT,
9 EVENT_ATTR_IBC_PACKET_TIMEOUT_TIMESTAMP,
10};
11
12use super::{
13 Event, EVENT_TYPE_IBC_ACK_PACKET, EVENT_TYPE_IBC_RECV_PACKET, EVENT_TYPE_IBC_SEND_PACKET,
14 EVENT_TYPE_IBC_TIMEOUT_PACKET, EVENT_TYPE_IBC_WRITE_ACK,
15};
16
17#[derive(Clone)]
18pub struct IbcPacket {
19 pub src_port_id: IbcPortId,
20 pub src_channel_id: IbcChannelId,
21 pub dst_port_id: IbcPortId,
22 pub dst_channel_id: IbcChannelId,
23 pub src_connection_id: IbcConnectionId,
24 pub dst_connection_id: IbcConnectionId,
25 pub sequence: u64,
26 pub timeout_height: IbcPacketTimeoutHeight,
27 pub timeout_timestamp: u64,
28 pub data: Option<Vec<u8>>,
29 pub ack: Option<Vec<u8>>,
30 pub kind: IbcPacketKind,
31}
32
33impl std::fmt::Debug for IbcPacket {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 writeln!(f, "src_port_id: {}", self.src_port_id)?;
37 writeln!(f, "src_channel_id: {}", self.src_channel_id)?;
38 writeln!(f, "dst_port_id: {}", self.dst_port_id)?;
39 writeln!(f, "dst_channel_id: {}", self.dst_channel_id)?;
40 writeln!(f, "src_connection_id: {}", self.src_connection_id)?;
41 writeln!(f, "dst_connection_id: {}", self.dst_connection_id)?;
42 writeln!(f, "sequence: {}", self.sequence)?;
43 writeln!(f, "timeout_height: {:?}", self.timeout_height)?;
44 writeln!(f, "timeout_timestamp: {}", self.timeout_timestamp)?;
45 if let Some(data) = &self.data {
46 writeln!(f, "data: {}", hex::encode(data))?;
47 }
48 if let Some(ack) = &self.ack {
49 writeln!(f, "ack: {}", hex::encode(ack))?;
50 }
51 writeln!(f, "kind: {:?}", self.kind)
52 }
53}
54
55impl IbcPacket {
56 pub fn invert(&mut self) {
57 std::mem::swap(&mut self.src_port_id, &mut self.dst_port_id);
58 std::mem::swap(&mut self.src_channel_id, &mut self.dst_channel_id);
59 std::mem::swap(&mut self.src_connection_id, &mut self.dst_connection_id);
60 }
61}
62
63#[derive(Clone, Debug)]
64pub enum IbcPacketTimeoutHeight {
65 None,
66 Revision { height: u64, revision: u64 },
67}
68
69#[derive(Clone, Debug, PartialEq, Eq)]
70pub enum IbcPacketKind {
71 Send,
72 Receive,
73 WriteAck,
74 Ack,
75 Timeout,
76}
77
78impl TryFrom<&Event<'_>> for IbcPacketKind {
79 type Error = anyhow::Error;
80
81 fn try_from(event: &Event) -> Result<Self> {
82 if event.is_type(EVENT_TYPE_IBC_SEND_PACKET) {
83 Ok(IbcPacketKind::Send)
84 } else if event.is_type(EVENT_TYPE_IBC_RECV_PACKET) {
85 Ok(IbcPacketKind::Receive)
86 } else if event.is_type(EVENT_TYPE_IBC_ACK_PACKET) {
87 Ok(IbcPacketKind::Ack)
88 } else if event.is_type(EVENT_TYPE_IBC_WRITE_ACK) {
89 Ok(IbcPacketKind::WriteAck)
90 } else if event.is_type(EVENT_TYPE_IBC_TIMEOUT_PACKET) {
91 Ok(IbcPacketKind::Timeout)
92 } else {
93 Err(anyhow!("not an IBC packet event type: {}", event.ty()))
94 }
95 }
96}
97
98impl<'a> TryFrom<&Event<'a>> for IbcPacket {
99 type Error = anyhow::Error;
100
101 fn try_from(event: &Event<'a>) -> Result<Self> {
102 let kind: IbcPacketKind = event.try_into()?;
103
104 #[derive(Default)]
105 struct IbcPacketBuilder {
106 pub src_port_id: Option<IbcPortId>,
107 pub src_channel_id: Option<IbcChannelId>,
108 pub dst_port_id: Option<IbcPortId>,
109 pub dst_channel_id: Option<IbcChannelId>,
110 pub connection_id: Option<IbcConnectionId>,
111 pub sequence: Option<u64>,
112 pub timeout_height: Option<IbcPacketTimeoutHeight>,
113 pub timeout_timestamp: Option<u64>,
114 pub data: Option<Vec<u8>>,
115 pub ack: Option<Vec<u8>>,
116 }
117
118 let mut builder = IbcPacketBuilder::default();
119
120 for attribute in event.attributes() {
122 if attribute.key() == EVENT_ATTR_IBC_PACKET_SRC_PORT {
123 builder.src_port_id = Some(IbcPortId::new(attribute.value()));
124 }
125
126 if attribute.key() == EVENT_ATTR_IBC_PACKET_SRC_CHANNEL {
127 builder.src_channel_id = Some(IbcChannelId::new(attribute.value()));
128 }
129
130 if attribute.key() == EVENT_ATTR_IBC_PACKET_DST_PORT {
131 builder.dst_port_id = Some(IbcPortId::new(attribute.value()));
132 }
133
134 if attribute.key() == EVENT_ATTR_IBC_PACKET_DST_CHANNEL {
135 builder.dst_channel_id = Some(IbcChannelId::new(attribute.value()));
136 }
137
138 if attribute.key() == EVENT_ATTR_IBC_CONNECTION_ID {
139 builder.connection_id = Some(IbcConnectionId::new(attribute.value()));
140 }
141
142 if attribute.key() == EVENT_ATTR_IBC_PACKET_SEQUENCE {
143 builder.sequence = Some(attribute.value().parse()?);
144 }
145
146 if attribute.key() == EVENT_ATTR_IBC_PACKET_TIMEOUT_HEIGHT {
147 let mut s = attribute.value().split('-');
149 let revision: u64 = s
150 .next()
151 .ok_or_else(|| anyhow!("missing revision"))?
152 .parse()?;
153 let height: u64 = s.next().ok_or_else(|| anyhow!("missing height"))?.parse()?;
154 if revision == 0 && height == 0 {
156 builder.timeout_height = Some(IbcPacketTimeoutHeight::None);
157 } else {
158 builder.timeout_height =
159 Some(IbcPacketTimeoutHeight::Revision { revision, height });
160 }
161 }
162
163 if attribute.key() == EVENT_ATTR_IBC_PACKET_TIMEOUT_TIMESTAMP {
164 builder.timeout_timestamp = Some(attribute.value().parse()?);
165 }
166
167 if attribute.key() == EVENT_ATTR_IBC_PACKET_DATA_HEX {
168 let data = hex::decode(attribute.value())?;
169 builder.data = Some(data);
170 }
171 if attribute.key() == EVENT_ATTR_IBC_PACKET_ACK_HEX {
172 let ack = hex::decode(attribute.value())?;
173 builder.ack = Some(ack);
174 }
175 }
176
177 let connection_id = builder
180 .connection_id
181 .ok_or_else(|| anyhow!("missing connection id"))?;
182
183 Ok(IbcPacket {
184 src_port_id: builder
185 .src_port_id
186 .ok_or_else(|| anyhow!("missing src port"))?,
187 src_channel_id: builder
188 .src_channel_id
189 .ok_or_else(|| anyhow!("missing src channel"))?,
190 dst_port_id: builder
191 .dst_port_id
192 .ok_or_else(|| anyhow!("missing dst port"))?,
193 dst_channel_id: builder
194 .dst_channel_id
195 .ok_or_else(|| anyhow!("missing dst channel"))?,
196 src_connection_id: connection_id.clone(),
197 dst_connection_id: connection_id.clone(),
198 sequence: builder
199 .sequence
200 .ok_or_else(|| anyhow!("missing sequence"))?,
201 timeout_height: builder
202 .timeout_height
203 .ok_or_else(|| anyhow!("missing timeout height"))?,
204 timeout_timestamp: builder
205 .timeout_timestamp
206 .ok_or_else(|| anyhow!("missing timeout timestamp"))?,
207 data: builder.data,
208 ack: builder.ack,
209 kind,
210 })
211 }
212}