penumbra_sdk_ibc/component/msg_handler/
channel_open_ack.rs

1use anyhow::Result;
2use async_trait::async_trait;
3use cnidarium::{StateRead, StateWrite};
4use ibc_types::core::{
5    channel::channel::State as ChannelState, channel::events, channel::msgs::MsgChannelOpenAck,
6    channel::ChannelEnd, channel::Counterparty, channel::PortId, connection::ConnectionEnd,
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 MsgChannelOpenAck {
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_a, &self.port_id_on_a)
37            .await?
38            .ok_or_else(|| anyhow::anyhow!("channel not found"))?;
39
40        channel_state_is_correct(&channel)?;
41
42        // TODO: capability authentication?
43
44        let connection = verify_channel_connection_open(&state, &channel).await?;
45
46        let expected_counterparty =
47            Counterparty::new(self.port_id_on_a.clone(), Some(self.chan_id_on_a.clone()));
48
49        let expected_connection_hops = vec![connection
50            .counterparty
51            .connection_id
52            .clone()
53            .ok_or_else(|| anyhow::anyhow!("no counterparty connection id provided"))?];
54
55        let expected_channel = ChannelEnd {
56            state: ChannelState::TryOpen,
57            ordering: channel.ordering,
58            remote: expected_counterparty,
59            connection_hops: expected_connection_hops,
60            version: self.version_on_b.clone(),
61            ..ChannelEnd::default()
62        };
63
64        state
65            .verify_channel_proof(
66                &connection,
67                &self.proof_chan_end_on_b,
68                &self.proof_height_on_b,
69                &self.chan_id_on_b,
70                &channel.remote.port_id,
71                &expected_channel,
72            )
73            .await?;
74
75        let transfer = PortId::transfer();
76        if self.port_id_on_a == transfer {
77            AH::chan_open_ack_check(&mut state, self).await?;
78        } else {
79            anyhow::bail!("invalid port id");
80        }
81
82        channel.set_state(ChannelState::Open);
83        channel.set_version(self.version_on_b.clone());
84        channel.set_counterparty_channel_id(self.chan_id_on_b.clone());
85        state.put_channel(&self.chan_id_on_a, &self.port_id_on_a, channel.clone());
86
87        state.record(
88            events::channel::OpenAck {
89                port_id: self.port_id_on_a.clone(),
90                channel_id: self.chan_id_on_a.clone(),
91                counterparty_channel_id: channel
92                    .counterparty()
93                    .channel_id
94                    .clone()
95                    .unwrap_or_default(),
96                counterparty_port_id: channel.counterparty().port_id.clone(),
97                connection_id: channel.connection_hops[0].clone(),
98            }
99            .into(),
100        );
101
102        let transfer = PortId::transfer();
103        if self.port_id_on_a == transfer {
104            AH::chan_open_ack_execute(state, self).await;
105        } else {
106            anyhow::bail!("invalid port id");
107        }
108
109        Ok(())
110    }
111}
112
113fn channel_state_is_correct(channel: &ChannelEnd) -> anyhow::Result<()> {
114    if channel.state == ChannelState::Init {
115        Ok(())
116    } else {
117        Err(anyhow::anyhow!("channel is not in the correct state"))
118    }
119}
120
121async fn verify_channel_connection_open<S: StateRead>(
122    state: S,
123    channel: &ChannelEnd,
124) -> anyhow::Result<ConnectionEnd> {
125    let connection = state
126        .get_connection(&channel.connection_hops[0])
127        .await?
128        .ok_or_else(|| anyhow::anyhow!("connection not found for channel"))?;
129
130    if connection.state != ConnectionState::Open {
131        Err(anyhow::anyhow!("connection for channel is not open"))
132    } else {
133        Ok(connection)
134    }
135}