ibc_testkit/relayer/
context.rs

1use ibc::core::channel::types::packet::Packet;
2use ibc::core::client::context::client_state::ClientStateValidation;
3use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId};
4use ibc::core::host::types::path::ChannelEndPath;
5use ibc::core::host::ValidationContext;
6use ibc::primitives::Signer;
7
8use crate::context::TestContext;
9use crate::hosts::{HostClientState, TestHost};
10use crate::relayer::utils::TypedRelayerOps;
11use crate::testapp::ibc::core::types::DefaultIbcStore;
12
13/// A relayer context that allows interaction between two [`TestContext`] instances.
14pub struct RelayerContext<A, B>
15where
16    A: TestHost,
17    B: TestHost,
18    HostClientState<A>: ClientStateValidation<DefaultIbcStore>,
19    HostClientState<B>: ClientStateValidation<DefaultIbcStore>,
20{
21    ctx_a: TestContext<A>,
22    ctx_b: TestContext<B>,
23}
24
25impl<A, B> RelayerContext<A, B>
26where
27    A: TestHost,
28    B: TestHost,
29    HostClientState<A>: ClientStateValidation<DefaultIbcStore>,
30    HostClientState<B>: ClientStateValidation<DefaultIbcStore>,
31{
32    /// Creates a new relayer context with the given [`TestContext`] instances.
33    pub fn new(ctx_a: TestContext<A>, ctx_b: TestContext<B>) -> Self {
34        Self { ctx_a, ctx_b }
35    }
36
37    /// Returns an immutable reference to the first context.
38    pub fn get_ctx_a(&self) -> &TestContext<A> {
39        &self.ctx_a
40    }
41
42    /// Returns an immutable reference to the second context.
43    pub fn get_ctx_b(&self) -> &TestContext<B> {
44        &self.ctx_b
45    }
46
47    /// Returns a mutable reference to the first context.
48    pub fn get_ctx_a_mut(&mut self) -> &mut TestContext<A> {
49        &mut self.ctx_a
50    }
51
52    /// Returns a mutable reference to the second context.
53    pub fn get_ctx_b_mut(&mut self) -> &mut TestContext<B> {
54        &mut self.ctx_b
55    }
56
57    /// Creates a light client of second context on the first context.
58    /// Returns the client identifier of the created client.
59    pub fn create_client_on_a(&mut self, signer: Signer) -> ClientId {
60        TypedRelayerOps::<A, B>::create_client_on_a(&mut self.ctx_a, &self.ctx_b, signer)
61    }
62
63    /// Creates a light client of first context on the second context.
64    /// Returns the client identifier of the created client.
65    pub fn create_client_on_b(&mut self, signer: Signer) -> ClientId {
66        TypedRelayerOps::<B, A>::create_client_on_a(&mut self.ctx_b, &self.ctx_a, signer)
67    }
68
69    /// Updates the client on the first context with the latest header of the second context.
70    pub fn update_client_on_a_with_sync(&mut self, client_id_on_a: ClientId, signer: Signer) {
71        TypedRelayerOps::<A, B>::update_client_on_a_with_sync(
72            &mut self.ctx_a,
73            &mut self.ctx_b,
74            client_id_on_a,
75            signer,
76        )
77    }
78
79    /// Updates the client on the second context with the latest header of the first context.
80    pub fn update_client_on_b_with_sync(&mut self, client_id_on_b: ClientId, signer: Signer) {
81        TypedRelayerOps::<B, A>::update_client_on_a_with_sync(
82            &mut self.ctx_b,
83            &mut self.ctx_a,
84            client_id_on_b,
85            signer,
86        )
87    }
88
89    /// Creates a connection between the two contexts starting from the first context.
90    /// Returns the connection identifiers of the created connection ends.
91    pub fn create_connection_on_a(
92        &mut self,
93        client_id_on_a: ClientId,
94        client_id_on_b: ClientId,
95        signer: Signer,
96    ) -> (ConnectionId, ConnectionId) {
97        TypedRelayerOps::<A, B>::create_connection_on_a(
98            &mut self.ctx_a,
99            &mut self.ctx_b,
100            client_id_on_a,
101            client_id_on_b,
102            signer,
103        )
104    }
105
106    /// Creates a connection between the two contexts starting from the second context.
107    /// Returns the connection identifiers of the created connection ends.
108    pub fn create_connection_on_b(
109        &mut self,
110        client_id_on_b: ClientId,
111        client_id_on_a: ClientId,
112        signer: Signer,
113    ) -> (ConnectionId, ConnectionId) {
114        TypedRelayerOps::<B, A>::create_connection_on_a(
115            &mut self.ctx_b,
116            &mut self.ctx_a,
117            client_id_on_b,
118            client_id_on_a,
119            signer,
120        )
121    }
122
123    /// Creates a channel between the two contexts starting from the first context.
124    /// Returns the channel identifiers of the created channel ends.
125    pub fn create_channel_on_a(
126        &mut self,
127        conn_id_on_a: ConnectionId,
128        port_id_on_a: PortId,
129        conn_id_on_b: ConnectionId,
130        port_id_on_b: PortId,
131        signer: Signer,
132    ) -> (ChannelId, ChannelId) {
133        let client_id_on_a = self
134            .ctx_a
135            .ibc_store()
136            .connection_end(&conn_id_on_a)
137            .expect("connection exists")
138            .client_id()
139            .clone();
140
141        let client_id_on_b = self
142            .ctx_b
143            .ibc_store()
144            .connection_end(&conn_id_on_b)
145            .expect("connection exists")
146            .client_id()
147            .clone();
148
149        TypedRelayerOps::<A, B>::create_channel_on_a(
150            &mut self.ctx_a,
151            &mut self.ctx_b,
152            client_id_on_a,
153            conn_id_on_a,
154            port_id_on_a,
155            client_id_on_b,
156            conn_id_on_b,
157            port_id_on_b,
158            signer,
159        )
160    }
161
162    /// Creates a channel between the two contexts starting from the second context.
163    /// Returns the channel identifiers of the created channel ends.
164    pub fn create_channel_on_b(
165        &mut self,
166        conn_id_on_b: ConnectionId,
167        port_id_on_b: PortId,
168        conn_id_on_a: ConnectionId,
169        port_id_on_a: PortId,
170        signer: Signer,
171    ) -> (ChannelId, ChannelId) {
172        let client_id_on_b = self
173            .ctx_b
174            .ibc_store()
175            .connection_end(&conn_id_on_b)
176            .expect("connection exists")
177            .client_id()
178            .clone();
179
180        let client_id_on_a = self
181            .ctx_a
182            .ibc_store()
183            .connection_end(&conn_id_on_a)
184            .expect("connection exists")
185            .client_id()
186            .clone();
187
188        TypedRelayerOps::<B, A>::create_channel_on_a(
189            &mut self.ctx_b,
190            &mut self.ctx_a,
191            client_id_on_b,
192            conn_id_on_b,
193            port_id_on_b,
194            client_id_on_a,
195            conn_id_on_a,
196            port_id_on_a,
197            signer,
198        )
199    }
200
201    /// Closes a channel between the two contexts starting from the first context.
202    pub fn close_channel_on_a(
203        &mut self,
204        chan_id_on_a: ChannelId,
205        port_id_on_a: PortId,
206        chan_id_on_b: ChannelId,
207        port_id_on_b: PortId,
208        signer: Signer,
209    ) {
210        let conn_id_on_a = self
211            .ctx_a
212            .ibc_store()
213            .channel_end(&ChannelEndPath::new(&port_id_on_a, &chan_id_on_a))
214            .expect("connection exists")
215            .connection_hops()[0]
216            .clone();
217
218        let conn_id_on_b = self
219            .ctx_b
220            .ibc_store()
221            .channel_end(&ChannelEndPath::new(&port_id_on_b, &chan_id_on_b))
222            .expect("connection exists")
223            .connection_hops()[0]
224            .clone();
225
226        let client_id_on_a = self
227            .ctx_a
228            .ibc_store()
229            .connection_end(&conn_id_on_a)
230            .expect("connection exists")
231            .client_id()
232            .clone();
233
234        let client_id_on_b = self
235            .ctx_b
236            .ibc_store()
237            .connection_end(&conn_id_on_b)
238            .expect("connection exists")
239            .client_id()
240            .clone();
241
242        TypedRelayerOps::<A, B>::close_channel_on_a(
243            &mut self.ctx_a,
244            &mut self.ctx_b,
245            client_id_on_a,
246            chan_id_on_a,
247            port_id_on_a,
248            client_id_on_b,
249            chan_id_on_b,
250            port_id_on_b,
251            signer,
252        )
253    }
254
255    /// Closes a channel between the two contexts starting from the second context.
256    pub fn close_channel_on_b(
257        &mut self,
258        chan_id_on_b: ChannelId,
259        port_id_on_b: PortId,
260        chan_id_on_a: ChannelId,
261        port_id_on_a: PortId,
262        signer: Signer,
263    ) {
264        let conn_id_on_b = self
265            .ctx_b
266            .ibc_store()
267            .channel_end(&ChannelEndPath::new(&port_id_on_b, &chan_id_on_b))
268            .expect("connection exists")
269            .connection_hops()[0]
270            .clone();
271
272        let conn_id_on_a = self
273            .ctx_a
274            .ibc_store()
275            .channel_end(&ChannelEndPath::new(&port_id_on_a, &chan_id_on_a))
276            .expect("connection exists")
277            .connection_hops()[0]
278            .clone();
279
280        let client_id_on_b = self
281            .ctx_b
282            .ibc_store()
283            .connection_end(&conn_id_on_b)
284            .expect("connection exists")
285            .client_id()
286            .clone();
287
288        let client_id_on_a = self
289            .ctx_a
290            .ibc_store()
291            .connection_end(&conn_id_on_a)
292            .expect("connection exists")
293            .client_id()
294            .clone();
295
296        TypedRelayerOps::<B, A>::close_channel_on_a(
297            &mut self.ctx_b,
298            &mut self.ctx_a,
299            client_id_on_b,
300            chan_id_on_b,
301            port_id_on_b,
302            client_id_on_a,
303            chan_id_on_a,
304            port_id_on_a,
305            signer,
306        )
307    }
308
309    /// Sends a packet from the first context to the second context by
310    /// submitting on receive packet on the second context.
311    ///
312    /// The IBC packet is created by an IBC application on the first context.
313    pub fn submit_packet_on_b(&mut self, packet: Packet, signer: Signer) {
314        let conn_id_on_a = self
315            .ctx_a
316            .ibc_store()
317            .channel_end(&ChannelEndPath::new(
318                &packet.port_id_on_a,
319                &packet.chan_id_on_a,
320            ))
321            .expect("connection exists")
322            .connection_hops()[0]
323            .clone();
324
325        let conn_id_on_b = self
326            .ctx_b
327            .ibc_store()
328            .channel_end(&ChannelEndPath::new(
329                &packet.port_id_on_b,
330                &packet.chan_id_on_b,
331            ))
332            .expect("connection exists")
333            .connection_hops()[0]
334            .clone();
335
336        let client_id_on_a = self
337            .ctx_a
338            .ibc_store()
339            .connection_end(&conn_id_on_a)
340            .expect("connection exists")
341            .client_id()
342            .clone();
343
344        let client_id_on_b = self
345            .ctx_b
346            .ibc_store()
347            .connection_end(&conn_id_on_b)
348            .expect("connection exists")
349            .client_id()
350            .clone();
351
352        TypedRelayerOps::<A, B>::submit_packet_on_b(
353            &mut self.ctx_a,
354            &mut self.ctx_b,
355            packet,
356            client_id_on_a,
357            client_id_on_b,
358            signer,
359        )
360    }
361
362    /// Times out a packet from the first context to the second context by waiting
363    /// for the timeout period and then sending a timeout packet to the first context.
364    ///
365    /// The IBC packet is created by an IBC application on the first context.
366    pub fn timeout_packet_from_a(&mut self, packet: Packet, signer: Signer) {
367        let conn_id_on_a = self
368            .ctx_a
369            .ibc_store()
370            .channel_end(&ChannelEndPath::new(
371                &packet.port_id_on_a,
372                &packet.chan_id_on_a,
373            ))
374            .expect("connection exists")
375            .connection_hops()[0]
376            .clone();
377
378        let conn_id_on_b = self
379            .ctx_b
380            .ibc_store()
381            .channel_end(&ChannelEndPath::new(
382                &packet.port_id_on_b,
383                &packet.chan_id_on_b,
384            ))
385            .expect("connection exists")
386            .connection_hops()[0]
387            .clone();
388
389        let client_id_on_a = self
390            .ctx_a
391            .ibc_store()
392            .connection_end(&conn_id_on_a)
393            .expect("connection exists")
394            .client_id()
395            .clone();
396
397        let client_id_on_b = self
398            .ctx_b
399            .ibc_store()
400            .connection_end(&conn_id_on_b)
401            .expect("connection exists")
402            .client_id()
403            .clone();
404
405        TypedRelayerOps::<A, B>::timeout_packet_from_a(
406            &mut self.ctx_a,
407            &mut self.ctx_b,
408            packet,
409            client_id_on_a,
410            client_id_on_b,
411            signer,
412        )
413    }
414
415    /// Timeouts a packet from the first context to the second context by closing the
416    /// corresponding channel is closed and then sending a timeout packet to the first context.
417    ///
418    /// The IBC packet is created by an IBC application on the first context.
419    pub fn timeout_packet_from_a_on_channel_close(&mut self, packet: Packet, signer: Signer) {
420        let conn_id_on_a = self
421            .ctx_a
422            .ibc_store()
423            .channel_end(&ChannelEndPath::new(
424                &packet.port_id_on_a,
425                &packet.chan_id_on_a,
426            ))
427            .expect("connection exists")
428            .connection_hops()[0]
429            .clone();
430
431        let conn_id_on_b = self
432            .ctx_b
433            .ibc_store()
434            .channel_end(&ChannelEndPath::new(
435                &packet.port_id_on_b,
436                &packet.chan_id_on_b,
437            ))
438            .expect("connection exists")
439            .connection_hops()[0]
440            .clone();
441
442        let client_id_on_a = self
443            .ctx_a
444            .ibc_store()
445            .connection_end(&conn_id_on_a)
446            .expect("connection exists")
447            .client_id()
448            .clone();
449
450        let client_id_on_b = self
451            .ctx_b
452            .ibc_store()
453            .connection_end(&conn_id_on_b)
454            .expect("connection exists")
455            .client_id()
456            .clone();
457
458        TypedRelayerOps::<A, B>::timeout_packet_from_a_on_channel_close(
459            &mut self.ctx_a,
460            &mut self.ctx_b,
461            packet,
462            client_id_on_a,
463            client_id_on_b,
464            signer,
465        )
466    }
467
468    /// Submit a
469    /// [`DummyTransferModule`](crate::testapp::ibc::applications::transfer::types::DummyTransferModule)
470    /// packet on the first context.
471    ///
472    /// Requires `serde` feature because of [`ibc::apps::transfer::handler::send_transfer`].
473    #[cfg(feature = "serde")]
474    pub fn send_dummy_transfer_packet_on_a(
475        &mut self,
476        chan_id_on_a: ChannelId,
477        signer: Signer,
478    ) -> Packet {
479        use ibc::apps::transfer::handler::send_transfer;
480        use ibc::apps::transfer::types::msgs::transfer::MsgTransfer;
481        use ibc::apps::transfer::types::packet::PacketData;
482        use ibc::core::channel::types::timeout::TimeoutTimestamp;
483        use ibc::core::handler::types::events::IbcEvent;
484
485        use crate::testapp::ibc::applications::transfer::types::DummyTransferModule;
486
487        // generate packet for DummyTransferModule
488        let packet_data = PacketData {
489            token: "1000uibc".parse().expect("valid prefixed coin"),
490            sender: signer.clone(),
491            receiver: signer.clone(),
492            memo: "sample memo".into(),
493        };
494
495        // packet with ibc metadata
496        // either height timeout or timestamp timeout must be set
497        let msg = MsgTransfer {
498            port_id_on_a: PortId::transfer(),
499            chan_id_on_a: chan_id_on_a.clone(),
500            packet_data,
501            // setting timeout height to 10 blocks from B's current height.
502            timeout_height_on_b: self.get_ctx_b().latest_height().add(10).into(),
503            // not setting timeout timestamp.
504            timeout_timestamp_on_b: TimeoutTimestamp::Never,
505        };
506
507        // module creates the send_packet
508        send_transfer(
509            self.get_ctx_a_mut().ibc_store_mut(),
510            &mut DummyTransferModule,
511            msg,
512        )
513        .expect("successfully created send_packet");
514
515        // send_packet wasn't committed, hence produce a block
516        self.get_ctx_a_mut().advance_block_height();
517
518        // retrieve the send_packet event
519        let Some(IbcEvent::SendPacket(send_packet_event)) = self
520            .get_ctx_a()
521            .ibc_store()
522            .events
523            .lock()
524            .iter()
525            .rev()
526            .nth(2)
527            .cloned()
528        else {
529            panic!("unexpected event")
530        };
531
532        // create the IBC packet type
533        Packet {
534            port_id_on_a: send_packet_event.port_id_on_a().clone(),
535            chan_id_on_a: send_packet_event.chan_id_on_a().clone(),
536            seq_on_a: *send_packet_event.seq_on_a(),
537            data: send_packet_event.packet_data().to_vec(),
538            timeout_height_on_b: *send_packet_event.timeout_height_on_b(),
539            timeout_timestamp_on_b: *send_packet_event.timeout_timestamp_on_b(),
540            port_id_on_b: send_packet_event.port_id_on_b().clone(),
541            chan_id_on_b: send_packet_event.chan_id_on_b().clone(),
542        }
543    }
544}