penumbra_sdk_ibc/component/msg_handler/
channel_open_ack.rs1use 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 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 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}