penumbra_sdk_ibc/component/msg_handler/
channel_open_confirm.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::MsgChannelOpenConfirm,
6    channel::ChannelEnd, channel::Counterparty, channel::PortId,
7    connection::State as ConnectionState,
8};
9
10use crate::component::{
11    app_handler::{AppHandlerCheck, AppHandlerExecute},
12    channel::{StateReadExt as _, StateWriteExt as _},
13    connection::StateReadExt as _,
14    proof_verification::ChannelProofVerifier,
15    HostInterface, MsgHandler,
16};
17
18#[async_trait]
19impl MsgHandler for MsgChannelOpenConfirm {
20    async fn check_stateless<H: AppHandlerCheck>(&self) -> Result<()> {
21        // NOTE: no additional stateless validation is possible
22
23        Ok(())
24    }
25
26    async fn try_execute<
27        S: StateWrite,
28        AH: AppHandlerCheck + AppHandlerExecute,
29        HI: HostInterface,
30    >(
31        &self,
32        mut state: S,
33    ) -> Result<()> {
34        tracing::debug!(msg = ?self);
35        let mut channel = state
36            .get_channel(&self.chan_id_on_b, &self.port_id_on_b)
37            .await?
38            .ok_or_else(|| anyhow::anyhow!("channel not found"))?;
39        if !channel.state_matches(&ChannelState::TryOpen) {
40            anyhow::bail!("channel is not in the correct state");
41        }
42
43        // TODO: capability authentication?
44
45        let connection = state
46            .get_connection(&channel.connection_hops[0])
47            .await?
48            .ok_or_else(|| anyhow::anyhow!("connection not found for channel"))?;
49        if !connection.state_matches(&ConnectionState::Open) {
50            anyhow::bail!("connection for channel is not open");
51        }
52
53        let expected_connection_hops = vec![connection
54            .counterparty
55            .connection_id
56            .clone()
57            .ok_or_else(|| anyhow::anyhow!("no counterparty connection id provided"))?];
58
59        let expected_counterparty =
60            Counterparty::new(self.port_id_on_b.clone(), Some(self.chan_id_on_b.clone()));
61
62        let expected_channel = ChannelEnd {
63            state: ChannelState::Open,
64            ordering: channel.ordering,
65            remote: expected_counterparty,
66            connection_hops: expected_connection_hops,
67            version: channel.version.clone(),
68            ..ChannelEnd::default()
69        };
70
71        state
72            .verify_channel_proof(
73                &connection,
74                &self.proof_chan_end_on_a,
75                &self.proof_height_on_a,
76                &channel
77                    .remote
78                    .channel_id
79                    .clone()
80                    .ok_or_else(|| anyhow::anyhow!("no channel id"))?,
81                &channel.remote.port_id.clone(),
82                &expected_channel,
83            )
84            .await?;
85
86        let transfer = PortId::transfer();
87        if self.port_id_on_b == transfer {
88            AH::chan_open_confirm_check(&mut state, self).await?;
89        } else {
90            anyhow::bail!("invalid port id");
91        }
92
93        channel.set_state(ChannelState::Open);
94        state.put_channel(&self.chan_id_on_b, &self.port_id_on_b, channel.clone());
95
96        state.record(
97            events::channel::OpenConfirm {
98                port_id: self.port_id_on_b.clone(),
99                channel_id: self.chan_id_on_b.clone(),
100                counterparty_port_id: channel.counterparty().port_id.clone(),
101                counterparty_channel_id: channel
102                    .counterparty()
103                    .channel_id
104                    .clone()
105                    .unwrap_or_default(),
106                connection_id: channel.connection_hops[0].clone(),
107            }
108            .into(),
109        );
110
111        let transfer = PortId::transfer();
112        if self.port_id_on_b == transfer {
113            AH::chan_open_confirm_execute(state, self).await;
114        } else {
115            anyhow::bail!("invalid port id");
116        }
117
118        Ok(())
119    }
120}