ibc_testkit/relayer/
utils.rs

1use alloc::string::String;
2use core::marker::PhantomData;
3use core::time::Duration;
4
5use ibc::core::channel::types::acknowledgement::Acknowledgement;
6use ibc::core::channel::types::channel::Order;
7use ibc::core::channel::types::msgs::{
8    ChannelMsg, MsgAcknowledgement, MsgChannelCloseConfirm, MsgChannelCloseInit, MsgChannelOpenAck,
9    MsgChannelOpenConfirm, MsgChannelOpenInit, MsgChannelOpenTry, MsgRecvPacket, MsgTimeout,
10    MsgTimeoutOnClose, PacketMsg,
11};
12use ibc::core::channel::types::packet::Packet;
13use ibc::core::channel::types::timeout::TimeoutHeight;
14use ibc::core::channel::types::Version as ChannelVersion;
15use ibc::core::client::context::client_state::ClientStateValidation;
16use ibc::core::client::context::ClientValidationContext;
17use ibc::core::client::types::msgs::{ClientMsg, MsgCreateClient, MsgUpdateClient};
18use ibc::core::connection::types::msgs::{
19    ConnectionMsg, MsgConnectionOpenAck, MsgConnectionOpenConfirm, MsgConnectionOpenInit,
20    MsgConnectionOpenTry,
21};
22use ibc::core::connection::types::version::Version as ConnectionVersion;
23use ibc::core::connection::types::Counterparty as ConnectionCounterParty;
24use ibc::core::handler::types::events::IbcEvent;
25use ibc::core::handler::types::msgs::MsgEnvelope;
26use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId};
27use ibc::core::host::types::path::{
28    AckPath, ChannelEndPath, ClientConsensusStatePath, ClientStatePath, CommitmentPath,
29    ConnectionPath, ReceiptPath,
30};
31use ibc::core::host::ValidationContext;
32use ibc::primitives::Signer;
33use ibc_query::core::context::ProvableContext;
34
35use crate::context::TestContext;
36use crate::hosts::{HostClientState, TestBlock, TestHost};
37use crate::testapp::ibc::core::types::{dummy_light_client, DefaultIbcStore};
38
39/// Implements IBC relayer functions for a pair of [`TestHost`] implementations: `A` and `B`.
40/// Note that, all the implementations are in one direction: from `A` to `B`.
41/// This ensures that the variable namings are consistent with the IBC message fields,
42/// leading to a less error-prone implementation.
43///
44/// For the functions in the opposite direction, use `TypedRelayerOps::<B, A>` instead of TypedRelayerOps::<A, B>`.
45#[derive(Debug, Default)]
46pub struct TypedRelayerOps<A, B>(PhantomData<A>, PhantomData<B>)
47where
48    A: TestHost,
49    B: TestHost,
50    HostClientState<A>: ClientStateValidation<DefaultIbcStore>,
51    HostClientState<B>: ClientStateValidation<DefaultIbcStore>;
52
53impl<A, B> TypedRelayerOps<A, B>
54where
55    A: TestHost,
56    B: TestHost,
57    HostClientState<A>: ClientStateValidation<DefaultIbcStore>,
58    HostClientState<B>: ClientStateValidation<DefaultIbcStore>,
59{
60    /// Creates a client on `A` with the state of `B`.
61    /// Returns the client identifier on `A`.
62    pub fn create_client_on_a(
63        ctx_a: &mut TestContext<A>,
64        ctx_b: &TestContext<B>,
65        signer: Signer,
66    ) -> ClientId {
67        let light_client_of_b = dummy_light_client(ctx_b).call();
68
69        let msg_for_a = MsgEnvelope::Client(ClientMsg::CreateClient(MsgCreateClient {
70            client_state: light_client_of_b.client_state.into(),
71            consensus_state: light_client_of_b
72                .consensus_states
73                .values()
74                .next()
75                .expect("at least one")
76                .clone()
77                .into(),
78            signer,
79        }));
80
81        ctx_a.deliver(msg_for_a).expect("success");
82
83        let Some(IbcEvent::CreateClient(create_client_b_event)) =
84            ctx_a.ibc_store().events.lock().last().cloned()
85        else {
86            panic!("unexpected event")
87        };
88
89        let client_id_on_a = create_client_b_event.client_id().clone();
90
91        assert_eq!(
92            ValidationContext::get_client_validation_context(ctx_a.ibc_store())
93                .client_state(&client_id_on_a)
94                .expect("client state exists")
95                .latest_height(),
96            ctx_b.latest_height()
97        );
98
99        client_id_on_a
100    }
101
102    /// Advances the block height on `A` until it catches up with the latest timestamp on `B`.
103    pub fn sync_clock_on_a(ctx_a: &mut TestContext<A>, ctx_b: &TestContext<B>) {
104        while ctx_b.latest_timestamp() > ctx_a.latest_timestamp() {
105            ctx_a.advance_block_height();
106        }
107    }
108
109    /// Updates the client on `A` with the latest header from `B`.
110    pub fn update_client_on_a(
111        ctx_a: &mut TestContext<A>,
112        ctx_b: &TestContext<B>,
113        client_id_on_a: ClientId,
114        signer: Signer,
115    ) {
116        let trusted_height_of_b = ctx_a
117            .ibc_store()
118            .get_client_validation_context()
119            .client_state(&client_id_on_a)
120            .expect("client state exists")
121            .latest_height();
122
123        let trusted_block_of_b = ctx_b
124            .host
125            .get_block(&trusted_height_of_b)
126            .expect("block exists");
127
128        let target_height_of_b = ctx_b.latest_height();
129
130        let target_block_of_b = ctx_b.host_block(&target_height_of_b).expect("block exists");
131
132        let msg_for_a = MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient {
133            client_id: client_id_on_a.clone(),
134            client_message: target_block_of_b
135                .into_header_with_trusted(&trusted_block_of_b)
136                .into(),
137            signer,
138        }));
139
140        ctx_a.deliver(msg_for_a).expect("success");
141
142        let Some(IbcEvent::UpdateClient(_)) = ctx_a.ibc_store().events.lock().last().cloned()
143        else {
144            panic!("unexpected event")
145        };
146    }
147
148    /// Updates the client on `A` with the latest header from `B` after syncing the timestamps.
149    ///
150    /// Timestamp sync is required, as IBC doesn't allow client updates from the future beyond max clock drift.
151    pub fn update_client_on_a_with_sync(
152        ctx_a: &mut TestContext<A>,
153        ctx_b: &mut TestContext<B>,
154        client_id_on_a: ClientId,
155        signer: Signer,
156    ) {
157        TypedRelayerOps::<A, B>::sync_clock_on_a(ctx_a, ctx_b);
158        TypedRelayerOps::<A, B>::update_client_on_a(ctx_a, ctx_b, client_id_on_a, signer);
159    }
160
161    /// `A` initiates a connection with the other end on `B`.
162    /// Returns the connection identifier on `A`.
163    pub fn connection_open_init_on_a(
164        ctx_a: &mut TestContext<A>,
165        ctx_b: &TestContext<B>,
166        client_id_on_a: ClientId,
167        client_id_on_b: ClientId,
168        signer: Signer,
169    ) -> ConnectionId {
170        let counterparty_b = ConnectionCounterParty::new(
171            client_id_on_b.clone(),
172            None,
173            ctx_b.ibc_store().commitment_prefix(),
174        );
175
176        let msg_for_a = MsgEnvelope::Connection(ConnectionMsg::OpenInit(MsgConnectionOpenInit {
177            client_id_on_a: client_id_on_a.clone(),
178            counterparty: counterparty_b,
179            version: None,
180            delay_period: Duration::from_secs(0),
181            signer: signer.clone(),
182        }));
183
184        ctx_a.deliver(msg_for_a).expect("success");
185
186        let Some(IbcEvent::OpenInitConnection(open_init_connection_event)) =
187            ctx_a.ibc_store().events.lock().last().cloned()
188        else {
189            panic!("unexpected event")
190        };
191
192        open_init_connection_event.conn_id_on_a().clone()
193    }
194
195    /// `B` receives the connection opening attempt by `A` after `A` initiates the connection.
196    /// Returns the connection identifier on `B`.
197    pub fn connection_open_try_on_b(
198        ctx_b: &mut TestContext<B>,
199        ctx_a: &TestContext<A>,
200        conn_id_on_a: ConnectionId,
201        client_id_on_a: ClientId,
202        client_id_on_b: ClientId,
203        signer: Signer,
204    ) -> ConnectionId {
205        let proofs_height_on_a = ctx_a.latest_height();
206
207        let client_state_of_b_on_a = ctx_a
208            .ibc_store()
209            .client_state(&client_id_on_a)
210            .expect("client state exists");
211
212        let consensus_height_of_b_on_a = client_state_of_b_on_a.latest_height();
213
214        let counterparty_a = ConnectionCounterParty::new(
215            client_id_on_a.clone(),
216            Some(conn_id_on_a.clone()),
217            ctx_a.ibc_store().commitment_prefix(),
218        );
219
220        let proof_conn_end_on_a = ctx_a
221            .ibc_store()
222            .get_proof(
223                proofs_height_on_a,
224                &ConnectionPath::new(&conn_id_on_a).into(),
225            )
226            .expect("connection end exists")
227            .try_into()
228            .expect("value merkle proof");
229
230        let proof_client_state_of_b_on_a = ctx_a
231            .ibc_store()
232            .get_proof(
233                proofs_height_on_a,
234                &ClientStatePath::new(client_id_on_a.clone()).into(),
235            )
236            .expect("client state exists")
237            .try_into()
238            .expect("value merkle proof");
239
240        let proof_consensus_state_of_b_on_a = ctx_a
241            .ibc_store()
242            .get_proof(
243                proofs_height_on_a,
244                &ClientConsensusStatePath::new(
245                    client_id_on_a.clone(),
246                    consensus_height_of_b_on_a.revision_number(),
247                    consensus_height_of_b_on_a.revision_height(),
248                )
249                .into(),
250            )
251            .expect("consensus state exists")
252            .try_into()
253            .expect("value merkle proof");
254
255        #[allow(deprecated)]
256        let msg_for_b = MsgEnvelope::Connection(ConnectionMsg::OpenTry(MsgConnectionOpenTry {
257            client_id_on_b: client_id_on_b.clone(),
258            client_state_of_b_on_a: client_state_of_b_on_a.into(),
259            counterparty: counterparty_a,
260            versions_on_a: ConnectionVersion::compatibles(),
261            proof_conn_end_on_a,
262            proof_client_state_of_b_on_a,
263            proof_consensus_state_of_b_on_a,
264            proofs_height_on_a,
265            consensus_height_of_b_on_a,
266            delay_period: Duration::from_secs(0),
267            signer: signer.clone(),
268            proof_consensus_state_of_b: None,
269            // deprecated
270            previous_connection_id: String::new(),
271        }));
272
273        ctx_b.deliver(msg_for_b).expect("success");
274
275        let Some(IbcEvent::OpenTryConnection(open_try_connection_event)) =
276            ctx_b.ibc_store().events.lock().last().cloned()
277        else {
278            panic!("unexpected event")
279        };
280
281        open_try_connection_event.conn_id_on_b().clone()
282    }
283
284    /// `A` receives `B`'s acknowledgement that `B` received the connection opening attempt by `A`.
285    /// `A` starts processing the connection on its side.
286    pub fn connection_open_ack_on_a(
287        ctx_a: &mut TestContext<A>,
288        ctx_b: &TestContext<B>,
289        conn_id_on_a: ConnectionId,
290        conn_id_on_b: ConnectionId,
291        client_id_on_b: ClientId,
292        signer: Signer,
293    ) {
294        let proofs_height_on_b = ctx_b.latest_height();
295
296        let client_state_of_a_on_b = ctx_b
297            .ibc_store()
298            .client_state(&client_id_on_b)
299            .expect("client state exists");
300
301        let consensus_height_of_a_on_b = client_state_of_a_on_b.latest_height();
302
303        let proof_conn_end_on_b = ctx_b
304            .ibc_store()
305            .get_proof(
306                proofs_height_on_b,
307                &ConnectionPath::new(&conn_id_on_b).into(),
308            )
309            .expect("connection end exists")
310            .try_into()
311            .expect("value merkle proof");
312
313        let proof_client_state_of_a_on_b = ctx_b
314            .ibc_store()
315            .get_proof(
316                proofs_height_on_b,
317                &ClientStatePath::new(client_id_on_b.clone()).into(),
318            )
319            .expect("client state exists")
320            .try_into()
321            .expect("value merkle proof");
322
323        let proof_consensus_state_of_a_on_b = ctx_b
324            .ibc_store()
325            .get_proof(
326                proofs_height_on_b,
327                &ClientConsensusStatePath::new(
328                    client_id_on_b.clone(),
329                    consensus_height_of_a_on_b.revision_number(),
330                    consensus_height_of_a_on_b.revision_height(),
331                )
332                .into(),
333            )
334            .expect("consensus state exists")
335            .try_into()
336            .expect("value merkle proof");
337
338        let msg_for_a = MsgEnvelope::Connection(ConnectionMsg::OpenAck(MsgConnectionOpenAck {
339            conn_id_on_a: conn_id_on_a.clone(),
340            conn_id_on_b: conn_id_on_b.clone(),
341            client_state_of_a_on_b: client_state_of_a_on_b.into(),
342            proof_conn_end_on_b,
343            proof_client_state_of_a_on_b,
344            proof_consensus_state_of_a_on_b,
345            proofs_height_on_b,
346            consensus_height_of_a_on_b,
347            version: ConnectionVersion::compatibles()[0].clone(),
348            signer: signer.clone(),
349            proof_consensus_state_of_a: None,
350        }));
351
352        ctx_a.deliver(msg_for_a).expect("success");
353
354        let Some(IbcEvent::OpenAckConnection(_)) = ctx_a.ibc_store().events.lock().last().cloned()
355        else {
356            panic!("unexpected event")
357        };
358    }
359
360    /// `B` receives the confirmation from `A` that the connection creation was successful.
361    /// `B` also starts processing the connection on its side.
362    pub fn connection_open_confirm_on_b(
363        ctx_b: &mut TestContext<B>,
364        ctx_a: &TestContext<A>,
365        conn_id_on_a: ConnectionId,
366        conn_id_on_b: ConnectionId,
367        signer: Signer,
368    ) {
369        let proof_height_on_a = ctx_a.latest_height();
370
371        let proof_conn_end_on_a = ctx_a
372            .ibc_store()
373            .get_proof(
374                proof_height_on_a,
375                &ConnectionPath::new(&conn_id_on_a).into(),
376            )
377            .expect("connection end exists")
378            .try_into()
379            .expect("value merkle proof");
380
381        let msg_for_b =
382            MsgEnvelope::Connection(ConnectionMsg::OpenConfirm(MsgConnectionOpenConfirm {
383                conn_id_on_b: conn_id_on_b.clone(),
384                proof_conn_end_on_a,
385                proof_height_on_a,
386                signer: signer.clone(),
387            }));
388
389        ctx_b.deliver(msg_for_b).expect("success");
390
391        let Some(IbcEvent::OpenConfirmConnection(_)) = ctx_b.ibc_store().events.lock().last()
392        else {
393            panic!("unexpected event")
394        };
395    }
396
397    /// A connection is created by `A` towards `B` using the IBC connection handshake protocol.
398    /// Returns the connection identifiers of `A` and `B`.
399    pub fn create_connection_on_a(
400        ctx_a: &mut TestContext<A>,
401        ctx_b: &mut TestContext<B>,
402        client_id_on_a: ClientId,
403        client_id_on_b: ClientId,
404        signer: Signer,
405    ) -> (ConnectionId, ConnectionId) {
406        let conn_id_on_a = TypedRelayerOps::<A, B>::connection_open_init_on_a(
407            ctx_a,
408            ctx_b,
409            client_id_on_a.clone(),
410            client_id_on_b.clone(),
411            signer.clone(),
412        );
413
414        TypedRelayerOps::<B, A>::update_client_on_a_with_sync(
415            ctx_b,
416            ctx_a,
417            client_id_on_b.clone(),
418            signer.clone(),
419        );
420
421        let conn_id_on_b = TypedRelayerOps::<A, B>::connection_open_try_on_b(
422            ctx_b,
423            ctx_a,
424            conn_id_on_a.clone(),
425            client_id_on_a.clone(),
426            client_id_on_b.clone(),
427            signer.clone(),
428        );
429
430        TypedRelayerOps::<A, B>::update_client_on_a_with_sync(
431            ctx_a,
432            ctx_b,
433            client_id_on_a.clone(),
434            signer.clone(),
435        );
436
437        TypedRelayerOps::<A, B>::connection_open_ack_on_a(
438            ctx_a,
439            ctx_b,
440            conn_id_on_a.clone(),
441            conn_id_on_b.clone(),
442            client_id_on_b.clone(),
443            signer.clone(),
444        );
445
446        TypedRelayerOps::<B, A>::update_client_on_a_with_sync(
447            ctx_b,
448            ctx_a,
449            client_id_on_b.clone(),
450            signer.clone(),
451        );
452
453        TypedRelayerOps::<A, B>::connection_open_confirm_on_b(
454            ctx_b,
455            ctx_a,
456            conn_id_on_b.clone(),
457            conn_id_on_a.clone(),
458            signer.clone(),
459        );
460
461        TypedRelayerOps::<A, B>::update_client_on_a_with_sync(ctx_a, ctx_b, client_id_on_a, signer);
462
463        (conn_id_on_a, conn_id_on_b)
464    }
465
466    /// `A` initiates a channel with port identifier with the other end on `B`.
467    /// Returns the channel identifier of `A`.
468    pub fn channel_open_init_on_a(
469        ctx_a: &mut TestContext<A>,
470        conn_id_on_a: ConnectionId,
471        port_id_on_a: PortId,
472        port_id_on_b: PortId,
473        signer: Signer,
474    ) -> ChannelId {
475        let msg_for_a = MsgEnvelope::Channel(ChannelMsg::OpenInit(MsgChannelOpenInit {
476            port_id_on_a,
477            connection_hops_on_a: [conn_id_on_a].to_vec(),
478            port_id_on_b,
479            ordering: Order::Unordered,
480            signer,
481            version_proposal: ChannelVersion::empty(),
482        }));
483
484        ctx_a.deliver(msg_for_a).expect("success");
485
486        let Some(IbcEvent::OpenInitChannel(open_init_channel_event)) =
487            ctx_a.ibc_store().events.lock().last().cloned()
488        else {
489            panic!("unexpected event")
490        };
491
492        open_init_channel_event.chan_id_on_a().clone()
493    }
494
495    /// `B` receives the channel opening attempt by `A` after `A` initiates the channel.
496    /// Returns the channel identifier of `B`.
497    pub fn channel_open_try_on_b(
498        ctx_b: &mut TestContext<B>,
499        ctx_a: &TestContext<A>,
500        conn_id_on_b: ConnectionId,
501        chan_id_on_a: ChannelId,
502        port_id_on_a: PortId,
503        signer: Signer,
504    ) -> ChannelId {
505        let proof_height_on_a = ctx_a.latest_height();
506
507        let proof_chan_end_on_a = ctx_a
508            .ibc_store()
509            .get_proof(
510                proof_height_on_a,
511                &ChannelEndPath::new(&port_id_on_a, &chan_id_on_a).into(),
512            )
513            .expect("connection end exists")
514            .try_into()
515            .expect("value merkle proof");
516
517        #[allow(deprecated)]
518        let msg_for_b = MsgEnvelope::Channel(ChannelMsg::OpenTry(MsgChannelOpenTry {
519            port_id_on_b: PortId::transfer(),
520            connection_hops_on_b: [conn_id_on_b].to_vec(),
521            port_id_on_a: PortId::transfer(),
522            chan_id_on_a,
523            version_supported_on_a: ChannelVersion::empty(),
524            proof_chan_end_on_a,
525            proof_height_on_a,
526            ordering: Order::Unordered,
527            signer,
528
529            version_proposal: ChannelVersion::empty(),
530        }));
531
532        ctx_b.deliver(msg_for_b).expect("success");
533
534        let Some(IbcEvent::OpenTryChannel(open_try_channel_event)) =
535            ctx_b.ibc_store().events.lock().last().cloned()
536        else {
537            panic!("unexpected event")
538        };
539
540        open_try_channel_event.chan_id_on_b().clone()
541    }
542
543    /// `A` receives `B`'s acknowledgement that `B` received the channel opening attempt by `A`.
544    /// `A` starts processing the channel on its side.
545    pub fn channel_open_ack_on_a(
546        ctx_a: &mut TestContext<A>,
547        ctx_b: &TestContext<B>,
548        chan_id_on_a: ChannelId,
549        port_id_on_a: PortId,
550        chan_id_on_b: ChannelId,
551        port_id_on_b: PortId,
552        signer: Signer,
553    ) {
554        let proof_height_on_b = ctx_b.latest_height();
555
556        let proof_chan_end_on_b = ctx_b
557            .ibc_store()
558            .get_proof(
559                proof_height_on_b,
560                &ChannelEndPath::new(&port_id_on_b, &chan_id_on_b).into(),
561            )
562            .expect("connection end exists")
563            .try_into()
564            .expect("value merkle proof");
565
566        let msg_for_a = MsgEnvelope::Channel(ChannelMsg::OpenAck(MsgChannelOpenAck {
567            port_id_on_a,
568            chan_id_on_a,
569            chan_id_on_b,
570            version_on_b: ChannelVersion::empty(),
571            proof_chan_end_on_b,
572            proof_height_on_b,
573            signer,
574        }));
575
576        ctx_a.deliver(msg_for_a).expect("success");
577
578        let Some(IbcEvent::OpenAckChannel(_)) = ctx_a.ibc_store().events.lock().last().cloned()
579        else {
580            panic!("unexpected event")
581        };
582    }
583
584    /// `B` receives the confirmation from `A` that the channel creation was successful.
585    /// `B` also starts processing the channel on its side.
586    pub fn channel_open_confirm_on_b(
587        ctx_b: &mut TestContext<B>,
588        ctx_a: &TestContext<A>,
589        chan_id_on_a: ChannelId,
590        chan_id_on_b: ChannelId,
591        port_id_on_b: PortId,
592        signer: Signer,
593    ) {
594        let proof_height_on_a = ctx_a.latest_height();
595
596        let proof_chan_end_on_a = ctx_a
597            .ibc_store()
598            .get_proof(
599                proof_height_on_a,
600                &ChannelEndPath::new(&PortId::transfer(), &chan_id_on_a).into(),
601            )
602            .expect("connection end exists")
603            .try_into()
604            .expect("value merkle proof");
605
606        let msg_for_b = MsgEnvelope::Channel(ChannelMsg::OpenConfirm(MsgChannelOpenConfirm {
607            port_id_on_b,
608            chan_id_on_b,
609            proof_chan_end_on_a,
610            proof_height_on_a,
611            signer,
612        }));
613
614        ctx_b.deliver(msg_for_b).expect("success");
615
616        let Some(IbcEvent::OpenConfirmChannel(_)) = ctx_b.ibc_store().events.lock().last().cloned()
617        else {
618            panic!("unexpected event")
619        };
620    }
621
622    /// `A` initiates the channel closing, with the other end on `B`.
623    /// `A` stops processing the channel.
624    pub fn channel_close_init_on_a(
625        ctx_a: &mut TestContext<A>,
626        chan_id_on_a: ChannelId,
627        port_id_on_a: PortId,
628        signer: Signer,
629    ) {
630        let msg_for_a = MsgEnvelope::Channel(ChannelMsg::CloseInit(MsgChannelCloseInit {
631            port_id_on_a,
632            chan_id_on_a,
633            signer,
634        }));
635
636        ctx_a.deliver(msg_for_a).expect("success");
637
638        let Some(IbcEvent::CloseInitChannel(_)) = ctx_a.ibc_store().events.lock().last().cloned()
639        else {
640            panic!("unexpected event")
641        };
642    }
643
644    /// `B` receives the channel closing attempt by `A` after `A` initiates the channel closing.
645    /// `B` also stops processing the channel.
646    pub fn channel_close_confirm_on_b(
647        ctx_b: &mut TestContext<B>,
648        ctx_a: &TestContext<A>,
649        chan_id_on_b: ChannelId,
650        port_id_on_b: PortId,
651        signer: Signer,
652    ) {
653        let proof_height_on_a = ctx_a.latest_height();
654
655        let proof_chan_end_on_a = ctx_a
656            .ibc_store()
657            .get_proof(
658                proof_height_on_a,
659                &ChannelEndPath::new(&PortId::transfer(), &chan_id_on_b).into(),
660            )
661            .expect("connection end exists")
662            .try_into()
663            .expect("value merkle proof");
664
665        let msg_for_b = MsgEnvelope::Channel(ChannelMsg::CloseConfirm(MsgChannelCloseConfirm {
666            port_id_on_b,
667            chan_id_on_b,
668            proof_chan_end_on_a,
669            proof_height_on_a,
670            signer,
671        }));
672
673        ctx_b.deliver(msg_for_b).expect("success");
674
675        let Some(IbcEvent::CloseConfirmChannel(_)) =
676            ctx_b.ibc_store().events.lock().last().cloned()
677        else {
678            panic!("unexpected event")
679        };
680    }
681
682    /// A channel is created by `A` towards `B` using the IBC channel handshake protocol.
683    /// Returns the channel identifiers of `A` and `B`.
684    #[allow(clippy::too_many_arguments)]
685    pub fn create_channel_on_a(
686        ctx_a: &mut TestContext<A>,
687        ctx_b: &mut TestContext<B>,
688        client_id_on_a: ClientId,
689        conn_id_on_a: ConnectionId,
690        port_id_on_a: PortId,
691        client_id_on_b: ClientId,
692        conn_id_on_b: ConnectionId,
693        port_id_on_b: PortId,
694        signer: Signer,
695    ) -> (ChannelId, ChannelId) {
696        let chan_id_on_a = TypedRelayerOps::<A, B>::channel_open_init_on_a(
697            ctx_a,
698            conn_id_on_a.clone(),
699            port_id_on_a.clone(),
700            port_id_on_b.clone(),
701            signer.clone(),
702        );
703
704        TypedRelayerOps::<B, A>::update_client_on_a_with_sync(
705            ctx_b,
706            ctx_a,
707            client_id_on_b.clone(),
708            signer.clone(),
709        );
710
711        let chan_id_on_b = TypedRelayerOps::<A, B>::channel_open_try_on_b(
712            ctx_b,
713            ctx_a,
714            conn_id_on_b.clone(),
715            chan_id_on_a.clone(),
716            port_id_on_a.clone(),
717            signer.clone(),
718        );
719
720        TypedRelayerOps::<A, B>::update_client_on_a_with_sync(
721            ctx_a,
722            ctx_b,
723            client_id_on_a.clone(),
724            signer.clone(),
725        );
726
727        TypedRelayerOps::<A, B>::channel_open_ack_on_a(
728            ctx_a,
729            ctx_b,
730            chan_id_on_a.clone(),
731            port_id_on_a.clone(),
732            chan_id_on_b.clone(),
733            port_id_on_b.clone(),
734            signer.clone(),
735        );
736
737        TypedRelayerOps::<B, A>::update_client_on_a_with_sync(
738            ctx_b,
739            ctx_a,
740            client_id_on_b.clone(),
741            signer.clone(),
742        );
743
744        TypedRelayerOps::<A, B>::channel_open_confirm_on_b(
745            ctx_b,
746            ctx_a,
747            chan_id_on_a.clone(),
748            chan_id_on_b.clone(),
749            port_id_on_b,
750            signer.clone(),
751        );
752
753        TypedRelayerOps::<A, B>::update_client_on_a_with_sync(ctx_a, ctx_b, client_id_on_a, signer);
754
755        (chan_id_on_a, chan_id_on_b)
756    }
757
758    /// A channel is closed by `A` towards `B` using the IBC channel handshake protocol.
759    #[allow(clippy::too_many_arguments)]
760    pub fn close_channel_on_a(
761        ctx_a: &mut TestContext<A>,
762        ctx_b: &mut TestContext<B>,
763        client_id_on_a: ClientId,
764        chan_id_on_a: ChannelId,
765        port_id_on_a: PortId,
766        client_id_on_b: ClientId,
767        chan_id_on_b: ChannelId,
768        port_id_on_b: PortId,
769        signer: Signer,
770    ) {
771        TypedRelayerOps::<A, B>::channel_close_init_on_a(
772            ctx_a,
773            chan_id_on_a.clone(),
774            port_id_on_a.clone(),
775            signer.clone(),
776        );
777
778        TypedRelayerOps::<B, A>::update_client_on_a_with_sync(
779            ctx_b,
780            ctx_a,
781            client_id_on_b,
782            signer.clone(),
783        );
784
785        TypedRelayerOps::<A, B>::channel_close_confirm_on_b(
786            ctx_b,
787            ctx_a,
788            chan_id_on_b,
789            port_id_on_b,
790            signer.clone(),
791        );
792
793        TypedRelayerOps::<A, B>::update_client_on_a_with_sync(ctx_a, ctx_b, client_id_on_a, signer);
794    }
795
796    /// `B` receives a packet from an IBC module on `A`.
797    /// Returns `B`'s acknowledgement of receipt.
798    pub fn packet_recv_on_b(
799        ctx_b: &mut TestContext<B>,
800        ctx_a: &TestContext<A>,
801        packet: Packet,
802        signer: Signer,
803    ) -> Acknowledgement {
804        let proof_height_on_a = ctx_a.latest_height();
805
806        let proof_commitment_on_a = ctx_a
807            .ibc_store()
808            .get_proof(
809                proof_height_on_a,
810                &CommitmentPath::new(&packet.port_id_on_a, &packet.chan_id_on_a, packet.seq_on_a)
811                    .into(),
812            )
813            .expect("commitment proof exists")
814            .try_into()
815            .expect("value merkle proof");
816
817        let msg_for_b = MsgEnvelope::Packet(PacketMsg::Recv(MsgRecvPacket {
818            packet,
819            proof_commitment_on_a,
820            proof_height_on_a,
821            signer,
822        }));
823
824        ctx_b.deliver(msg_for_b).expect("success");
825
826        let Some(IbcEvent::WriteAcknowledgement(write_ack_event)) =
827            ctx_b.ibc_store().events.lock().last().cloned()
828        else {
829            panic!("unexpected event")
830        };
831
832        write_ack_event.acknowledgement().clone()
833    }
834
835    /// `A` receives the acknowledgement from `B` that `B` received the packet from `A`.
836    pub fn packet_ack_on_a(
837        ctx_a: &mut TestContext<A>,
838        ctx_b: &TestContext<B>,
839        packet: Packet,
840        acknowledgement: Acknowledgement,
841        signer: Signer,
842    ) {
843        let proof_height_on_b = ctx_b.latest_height();
844
845        let proof_acked_on_b = ctx_b
846            .ibc_store()
847            .get_proof(
848                proof_height_on_b,
849                &AckPath::new(&packet.port_id_on_b, &packet.chan_id_on_b, packet.seq_on_a).into(),
850            )
851            .expect("acknowledgement proof exists")
852            .try_into()
853            .expect("value merkle proof");
854
855        let msg_for_a = MsgEnvelope::Packet(PacketMsg::Ack(MsgAcknowledgement {
856            packet,
857            acknowledgement,
858            proof_acked_on_b,
859            proof_height_on_b,
860            signer,
861        }));
862
863        ctx_a.deliver(msg_for_a).expect("success");
864
865        let Some(IbcEvent::AcknowledgePacket(_)) = ctx_a.ibc_store().events.lock().last().cloned()
866        else {
867            panic!("unexpected event")
868        };
869    }
870
871    /// `A` receives the timeout packet from `B`.
872    /// That is, `B` has not received the packet from `A` within the timeout period.
873    pub fn packet_timeout_on_a(
874        ctx_a: &mut TestContext<A>,
875        ctx_b: &TestContext<B>,
876        packet: Packet,
877        signer: Signer,
878    ) {
879        let proof_height_on_b = ctx_b.latest_height();
880
881        let proof_unreceived_on_b = ctx_b
882            .ibc_store()
883            .get_proof(
884                proof_height_on_b,
885                &ReceiptPath::new(&packet.port_id_on_b, &packet.chan_id_on_b, packet.seq_on_a)
886                    .into(),
887            )
888            .expect("non-membership receipt proof exists")
889            .try_into()
890            .expect("value merkle proof");
891
892        let msg_for_a = MsgEnvelope::Packet(PacketMsg::Timeout(MsgTimeout {
893            next_seq_recv_on_b: packet.seq_on_a,
894            packet,
895            proof_unreceived_on_b,
896            proof_height_on_b,
897            signer,
898        }));
899
900        ctx_a.deliver(msg_for_a).expect("success");
901
902        let Some(IbcEvent::TimeoutPacket(_)) = ctx_a.ibc_store().events.lock().last().cloned()
903        else {
904            panic!("unexpected event")
905        };
906    }
907
908    /// `A` receives the timeout packet from `B` after closing the channel.
909    /// That is, `B` has not received the packet from `A` because the channel is closed.
910    pub fn packet_timeout_on_close_on_a(
911        ctx_a: &mut TestContext<A>,
912        ctx_b: &TestContext<B>,
913        packet: Packet,
914        chan_id_on_b: ChannelId,
915        port_id_on_b: PortId,
916        signer: Signer,
917    ) {
918        let proof_height_on_b = ctx_b.latest_height();
919
920        let proof_unreceived_on_b = ctx_b
921            .ibc_store()
922            .get_proof(
923                proof_height_on_b,
924                &ReceiptPath::new(&port_id_on_b, &chan_id_on_b, packet.seq_on_a).into(),
925            )
926            .expect("non-membership receipt proof")
927            .try_into()
928            .expect("value merkle proof");
929
930        let proof_close_on_b = ctx_b
931            .ibc_store()
932            .get_proof(
933                proof_height_on_b,
934                &ChannelEndPath::new(&port_id_on_b, &chan_id_on_b).into(),
935            )
936            .expect("channel end data exists")
937            .try_into()
938            .expect("value merkle proof");
939
940        let msg_for_a = MsgEnvelope::Packet(PacketMsg::TimeoutOnClose(MsgTimeoutOnClose {
941            next_seq_recv_on_b: packet.seq_on_a,
942            packet,
943            proof_unreceived_on_b,
944            proof_close_on_b,
945            proof_height_on_b,
946            signer,
947        }));
948
949        ctx_a.deliver(msg_for_a).expect("success");
950
951        let Some(IbcEvent::TimeoutPacket(_)) = ctx_a.ibc_store().events.lock().last().cloned()
952        else {
953            panic!("unexpected event")
954        };
955    }
956
957    /// Sends a packet from an IBC application on `A` to `B` using the IBC packet relay protocol.
958    pub fn submit_packet_on_b(
959        ctx_a: &mut TestContext<A>,
960        ctx_b: &mut TestContext<B>,
961        packet: Packet,
962        client_id_on_a: ClientId,
963        client_id_on_b: ClientId,
964        signer: Signer,
965    ) {
966        // packet is passed from module
967
968        TypedRelayerOps::<B, A>::update_client_on_a_with_sync(
969            ctx_b,
970            ctx_a,
971            client_id_on_b.clone(),
972            signer.clone(),
973        );
974
975        let acknowledgement =
976            TypedRelayerOps::<A, B>::packet_recv_on_b(ctx_b, ctx_a, packet.clone(), signer.clone());
977
978        TypedRelayerOps::<A, B>::update_client_on_a_with_sync(
979            ctx_a,
980            ctx_b,
981            client_id_on_a,
982            signer.clone(),
983        );
984
985        TypedRelayerOps::<A, B>::packet_ack_on_a(
986            ctx_a,
987            ctx_b,
988            packet,
989            acknowledgement,
990            signer.clone(),
991        );
992
993        TypedRelayerOps::<B, A>::update_client_on_a_with_sync(
994            ctx_b,
995            ctx_a,
996            client_id_on_b,
997            signer.clone(),
998        );
999    }
1000
1001    /// Times out a packet from an IBC application on `A` to `B` after waiting timeout period.
1002    pub fn timeout_packet_from_a(
1003        ctx_a: &mut TestContext<A>,
1004        ctx_b: &mut TestContext<B>,
1005        packet: Packet,
1006        client_id_on_a: ClientId,
1007        client_id_on_b: ClientId,
1008        signer: Signer,
1009    ) {
1010        // packet is passed from module
1011
1012        let TimeoutHeight::At(timeout_height) = packet.timeout_height_on_b else {
1013            panic!("timeout height is set")
1014        };
1015
1016        // progress `B`'s block height to the timeout height.
1017        // packet is timed out at the timeout height + 1
1018        while ctx_b.latest_height() <= timeout_height {
1019            ctx_b.advance_block_height();
1020        }
1021
1022        // update client on `A` with the latest header from `B`.
1023        TypedRelayerOps::<A, B>::update_client_on_a_with_sync(
1024            ctx_a,
1025            ctx_b,
1026            client_id_on_a.clone(),
1027            signer.clone(),
1028        );
1029
1030        // timeout the packet on `A`.
1031        TypedRelayerOps::<A, B>::packet_timeout_on_a(ctx_a, ctx_b, packet.clone(), signer.clone());
1032
1033        // `A` has progressed; update client on `B` with the latest header from `A`.
1034        TypedRelayerOps::<B, A>::update_client_on_a_with_sync(
1035            ctx_b,
1036            ctx_a,
1037            client_id_on_b,
1038            signer.clone(),
1039        );
1040    }
1041
1042    /// Times out a packet from an IBC application on `A` to `B` after closing the channel.
1043    pub fn timeout_packet_from_a_on_channel_close(
1044        ctx_a: &mut TestContext<A>,
1045        ctx_b: &mut TestContext<B>,
1046        packet: Packet,
1047        client_id_on_a: ClientId,
1048        client_id_on_b: ClientId,
1049        signer: Signer,
1050    ) {
1051        // packet is passed from module
1052
1053        // close the channel on `A`.
1054        TypedRelayerOps::<A, B>::close_channel_on_a(
1055            ctx_a,
1056            ctx_b,
1057            client_id_on_a.clone(),
1058            packet.chan_id_on_a.clone(),
1059            packet.port_id_on_a.clone(),
1060            client_id_on_b.clone(),
1061            packet.chan_id_on_b.clone(),
1062            packet.port_id_on_b.clone(),
1063            signer.clone(),
1064        );
1065
1066        // timeout the packet on `A`.
1067        TypedRelayerOps::<A, B>::packet_timeout_on_close_on_a(
1068            ctx_a,
1069            ctx_b,
1070            packet.clone(),
1071            packet.chan_id_on_b.clone(),
1072            packet.port_id_on_b.clone(),
1073            signer.clone(),
1074        );
1075
1076        // `A` has progressed; update client on `B` with the latest header from `A`.
1077        TypedRelayerOps::<B, A>::update_client_on_a_with_sync(
1078            ctx_b,
1079            ctx_a,
1080            client_id_on_b,
1081            signer.clone(),
1082        );
1083    }
1084}