ibc_testkit/fixtures/core/channel/
packet.rs

1use bon::builder;
2use ibc::core::channel::types::packet::Packet;
3use ibc::core::channel::types::proto::v1::Packet as RawPacket;
4use ibc::core::channel::types::timeout::{TimeoutHeight, TimeoutTimestamp};
5use ibc::core::client::types::proto::v1::Height as RawHeight;
6use ibc::core::host::types::identifiers::{ChannelId, PortId, Sequence};
7use ibc::core::primitives::prelude::*;
8
9/// Returns a dummy [`Packet`], for testing purposes only!
10#[builder]
11pub fn dummy_packet(
12    #[builder(default = Sequence::from(0))] seq_on_a: Sequence,
13    #[builder(default = PortId::transfer())] port_id_on_a: PortId,
14    #[builder(default = ChannelId::zero())] chan_id_on_a: ChannelId,
15    #[builder(default = PortId::transfer())] port_id_on_b: PortId,
16    #[builder(default = ChannelId::zero())] chan_id_on_b: ChannelId,
17    #[builder(default)] data: Vec<u8>,
18    #[builder(default = TimeoutHeight::Never)] timeout_height_on_b: TimeoutHeight,
19    #[builder(default = TimeoutTimestamp::Never)] timeout_timestamp_on_b: TimeoutTimestamp,
20) -> Packet {
21    Packet {
22        seq_on_a,
23        port_id_on_a,
24        chan_id_on_a,
25        port_id_on_b,
26        chan_id_on_b,
27        data,
28        timeout_height_on_b,
29        timeout_timestamp_on_b,
30    }
31}
32
33/// Returns a dummy `RawPacket`, for testing purposes only!
34pub fn dummy_raw_packet(timeout_height: u64, timeout_timestamp: u64) -> RawPacket {
35    RawPacket {
36        sequence: 1,
37        source_port: PortId::transfer().to_string(),
38        source_channel: ChannelId::zero().to_string(),
39        destination_port: PortId::transfer().to_string(),
40        destination_channel: ChannelId::zero().to_string(),
41        data: vec![0],
42        timeout_height: Some(RawHeight {
43            revision_number: 0,
44            revision_height: timeout_height,
45        }),
46        timeout_timestamp,
47    }
48}
49
50pub fn dummy_proof() -> Vec<u8> {
51    b"Y29uc2Vuc3VzU3RhdGUvaWJjb25lY2xpZW50LzIy".to_vec()
52}
53
54#[cfg(test)]
55mod tests {
56    use ibc::core::channel::types::channel::Order;
57    use ibc::core::channel::types::events::SendPacket;
58    use ibc::core::handler::types::events::IbcEvent;
59    use ibc::core::host::types::identifiers::ConnectionId;
60
61    use super::*;
62
63    #[test]
64    fn packet_try_from_raw() {
65        struct Test {
66            name: String,
67            raw: RawPacket,
68            want_pass: bool,
69        }
70
71        let proof_height = 10;
72        let default_raw_packet = dummy_raw_packet(proof_height, 1000);
73        let raw_packet_no_timeout_timestamp = dummy_raw_packet(10, 0);
74
75        let mut raw_packet_invalid_timeout_height = dummy_raw_packet(0, 10);
76        raw_packet_invalid_timeout_height.timeout_height = Some(RawHeight {
77            revision_number: 1,
78            revision_height: 0,
79        });
80
81        let tests: Vec<Test> = vec![
82            Test {
83                name: "Good parameters".to_string(),
84                raw: default_raw_packet.clone(),
85                want_pass: true,
86            },
87            Test {
88                // Note: ibc-go currently (July 2022) incorrectly rejects this
89                // case, even though it is allowed in ICS-4.
90                name: "Packet with no timeout timestamp".to_string(),
91                raw: raw_packet_no_timeout_timestamp.clone(),
92                want_pass: true,
93            },
94            Test {
95                name: "Packet with invalid timeout height".to_string(),
96                raw: raw_packet_invalid_timeout_height,
97                want_pass: false,
98            },
99            Test {
100                name: "Src port validation: correct".to_string(),
101                raw: RawPacket {
102                    source_port: "srcportp34".to_string(),
103                    ..default_raw_packet.clone()
104                },
105                want_pass: true,
106            },
107            Test {
108                name: "Bad src port, name too short".to_string(),
109                raw: RawPacket {
110                    source_port: "p".to_string(),
111                    ..default_raw_packet.clone()
112                },
113                want_pass: false,
114            },
115            Test {
116                name: "Bad src port, name too long".to_string(),
117                raw: RawPacket {
118                    source_port: "abcdefghijasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfadgasgasdfasdfasdfasdfaklmnopqrstuabcdefghijasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfadgasgasdfasdfasdfasdfaklmnopqrstu".to_string(),
119                    ..default_raw_packet.clone()
120                },
121                want_pass: false,
122            },
123            Test {
124                name: "Dst port validation: correct".to_string(),
125                raw: RawPacket {
126                    destination_port: "destportsrcp34".to_string(),
127                    ..default_raw_packet.clone()
128                },
129                want_pass: true,
130            },
131            Test {
132                name: "Bad dst port, name too short".to_string(),
133                raw: RawPacket {
134                    destination_port: "p".to_string(),
135                    ..default_raw_packet.clone()
136                },
137                want_pass: false,
138            },
139            Test {
140                name: "Bad dst port, name too long".to_string(),
141                raw: RawPacket {
142                    destination_port: "abcdefghijasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfadgasgasdfasdfasdfasdfaklmnopqrstuabcdefghijasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfadgasgasdfas".to_string(),
143                    ..default_raw_packet.clone()
144                },
145                want_pass: false,
146            },
147            Test {
148                name: "Src channel validation: correct".to_string(),
149                raw: RawPacket {
150                    source_channel: "channel-1".to_string(),
151                    ..default_raw_packet.clone()
152                },
153                want_pass: true,
154            },
155            Test {
156                name: "Bad src channel, name too short".to_string(),
157                raw: RawPacket {
158                    source_channel: "p".to_string(),
159                    ..default_raw_packet.clone()
160                },
161                want_pass: false,
162            },
163            Test {
164                name: "Bad src channel, name too long".to_string(),
165                raw: RawPacket {
166                    source_channel: "channel-128391283791827398127398791283912837918273981273987912839".to_string(),
167                    ..default_raw_packet.clone()
168                },
169                want_pass: false,
170            },
171            Test {
172                name: "Dst channel validation: correct".to_string(),
173                raw: RawPacket {
174                    destination_channel: "channel-34".to_string(),
175                    ..default_raw_packet.clone()
176                },
177                want_pass: true,
178            },
179            Test {
180                name: "Bad dst channel, name too short".to_string(),
181                raw: RawPacket {
182                    destination_channel: "p".to_string(),
183                    ..default_raw_packet.clone()
184                },
185                want_pass: false,
186            },
187            Test {
188                name: "Bad dst channel, name too long".to_string(),
189                raw: RawPacket {
190                    destination_channel: "channel-128391283791827398127398791283912837918273981273987912839".to_string(),
191                    ..default_raw_packet.clone()
192                },
193                want_pass: false,
194            },
195            // Note: `timeout_height == None` is a quirk of protobuf. In
196            // `proto3` syntax, all structs are "nullable" by default and are
197            // represented as `Option<T>`. `ibc-go` defines the protobuf file
198            // with the extension option `gogoproto.nullable = false`, which
199            // means that they will always generate a field. It is left
200            // unspecified what a `None` value means. In this case, I believe it
201            // is best to assume the obvious semantic of "no timeout".
202            Test {
203                name: "Missing timeout height".to_string(),
204                raw: RawPacket {
205                    timeout_height: None,
206                    ..default_raw_packet
207                },
208                want_pass: true,
209            },
210            Test {
211                name: "Missing both timeout height and timestamp".to_string(),
212                raw: RawPacket {
213                    timeout_height: None,
214                    ..raw_packet_no_timeout_timestamp
215                },
216                want_pass: false,
217            }
218        ];
219
220        for test in tests {
221            let res_msg = Packet::try_from(test.raw.clone());
222
223            assert_eq!(
224                test.want_pass,
225                res_msg.is_ok(),
226                "Packet::try_from failed for test {}, \nraw packet {:?} with error {:?}",
227                test.name,
228                test.raw,
229                res_msg.err(),
230            );
231        }
232    }
233
234    #[test]
235    fn to_and_from() {
236        let raw = dummy_raw_packet(15, 0);
237        let msg = Packet::try_from(raw.clone()).unwrap();
238        let raw_back = RawPacket::from(msg.clone());
239        let msg_back = Packet::try_from(raw_back.clone()).unwrap();
240        assert_eq!(raw, raw_back);
241        assert_eq!(msg, msg_back);
242    }
243
244    #[test]
245    /// Ensures that we don't panic when packet data is not valid UTF-8.
246    /// See issue [#199](https://github.com/cosmos/ibc-rs/issues/199)
247    pub fn test_packet_data_non_utf8() {
248        let mut packet = Packet::try_from(dummy_raw_packet(1, 1)).unwrap();
249        packet.data = vec![128];
250
251        let ibc_event = IbcEvent::SendPacket(SendPacket::new(
252            packet,
253            Order::Unordered,
254            ConnectionId::zero(),
255        ));
256        let _ = tendermint::abci::Event::try_from(ibc_event);
257    }
258}