penumbra_sdk_ibc/component/msg_handler/
channel_close_init.rs

1use anyhow::Result;
2use async_trait::async_trait;
3use cnidarium::StateWrite;
4use ibc_types::core::{
5    channel::channel::State as ChannelState, channel::events, channel::msgs::MsgChannelCloseInit,
6    channel::PortId, connection::State as ConnectionState,
7};
8
9use crate::component::{
10    app_handler::{AppHandlerCheck, AppHandlerExecute},
11    channel::{StateReadExt as _, StateWriteExt as _},
12    connection::StateReadExt as _,
13    HostInterface, MsgHandler,
14};
15
16#[async_trait]
17impl MsgHandler for MsgChannelCloseInit {
18    async fn check_stateless<H: AppHandlerCheck>(&self) -> Result<()> {
19        // NOTE: no additional stateless validation is possible
20
21        Ok(())
22    }
23
24    async fn try_execute<
25        S: StateWrite,
26        AH: AppHandlerCheck + AppHandlerExecute,
27        HI: HostInterface,
28    >(
29        &self,
30        mut state: S,
31    ) -> Result<()> {
32        tracing::debug!(msg = ?self);
33        // TODO: capability authentication?
34        //
35        // we probably do need capability authentication here, or some other authorization
36        // method, to prevent anyone from spuriously closing channels.
37        //
38        let mut channel = state
39            .get_channel(&self.chan_id_on_a, &self.port_id_on_a)
40            .await?
41            .ok_or_else(|| anyhow::anyhow!("channel not found"))?;
42        if channel.state_matches(&ChannelState::Closed) {
43            anyhow::bail!("channel is already closed");
44        }
45
46        let connection = state
47            .get_connection(&channel.connection_hops[0])
48            .await?
49            .ok_or_else(|| anyhow::anyhow!("connection not found for channel"))?;
50        if !connection.state_matches(&ConnectionState::Open) {
51            anyhow::bail!("connection for channel is not open");
52        }
53        let transfer = PortId::transfer();
54        if self.port_id_on_a == transfer {
55            AH::chan_close_init_check(&mut state, self).await?;
56        } else {
57            anyhow::bail!("invalid port id");
58        }
59
60        channel.set_state(ChannelState::Closed);
61        state.put_channel(&self.chan_id_on_a, &self.port_id_on_a, channel.clone());
62
63        state.record(
64            events::channel::CloseInit {
65                port_id: self.port_id_on_a.clone(),
66                channel_id: self.chan_id_on_a.clone(),
67                counterparty_port_id: channel.counterparty().port_id.clone(),
68                counterparty_channel_id: channel
69                    .counterparty()
70                    .channel_id
71                    .clone()
72                    .unwrap_or_default(),
73                connection_id: channel.connection_hops[0].clone(),
74            }
75            .into(),
76        );
77
78        let transfer = PortId::transfer();
79        if self.port_id_on_a == transfer {
80            AH::chan_close_init_execute(state, self).await;
81        } else {
82            anyhow::bail!("invalid port id");
83        }
84
85        Ok(())
86    }
87}