penumbra_sdk_ibc/component/msg_handler/
channel_open_try.rs1use anyhow::{Context, Result};
2use async_trait::async_trait;
3use cnidarium::{StateRead, StateWrite};
4use ibc_types::core::{
5 channel::{
6 channel::State as ChannelState, events, msgs::MsgChannelOpenTry, ChannelEnd, Counterparty,
7 PortId,
8 },
9 connection::{ConnectionEnd, State as ConnectionState},
10};
11
12use crate::component::{
13 app_handler::{AppHandlerCheck, AppHandlerExecute},
14 channel::StateWriteExt,
15 connection::StateReadExt,
16 proof_verification::ChannelProofVerifier,
17 HostInterface, MsgHandler,
18};
19
20#[async_trait]
21impl MsgHandler for MsgChannelOpenTry {
22 async fn check_stateless<H: AppHandlerCheck>(&self) -> Result<()> {
23 connection_hops_eq_1(self)?;
24
25 Ok(())
26 }
27
28 async fn try_execute<
29 S: StateWrite,
30 AH: AppHandlerCheck + AppHandlerExecute,
31 HI: HostInterface,
32 >(
33 &self,
34 mut state: S,
35 ) -> Result<()> {
36 tracing::debug!(msg = ?self);
37 let connection_on_b = verify_connections_open(&state, self).await?;
38
39 let expected_channel_on_a = ChannelEnd {
43 state: ChannelState::Init,
44 ordering: self.ordering,
45 remote: Counterparty::new(self.port_id_on_b.clone(), None),
46 connection_hops: vec![connection_on_b
47 .counterparty
48 .connection_id
49 .clone()
50 .ok_or_else(|| anyhow::anyhow!("no counterparty connection id provided"))?],
51 version: self.version_supported_on_a.clone(),
52 ..ChannelEnd::default()
53 };
54
55 tracing::debug!(?self, ?expected_channel_on_a);
56
57 state
58 .verify_channel_proof(
59 &connection_on_b,
60 &self.proof_chan_end_on_a,
61 &self.proof_height_on_a,
62 &self.chan_id_on_a,
63 &self.port_id_on_a,
64 &expected_channel_on_a,
65 )
66 .await?;
67
68 let transfer = PortId::transfer();
69 if self.port_id_on_b == transfer {
70 AH::chan_open_try_check(&mut state, self).await?;
71 } else {
72 anyhow::bail!("invalid port id");
73 }
74
75 let channel_id = state
76 .next_channel_id()
77 .await
78 .context("unable to retrieve next channel id")?;
79 let new_channel = ChannelEnd {
80 state: ChannelState::TryOpen,
81 ordering: self.ordering,
82 remote: Counterparty::new(self.port_id_on_a.clone(), Some(self.chan_id_on_a.clone())),
83 connection_hops: self.connection_hops_on_b.clone(),
84 version: self.version_supported_on_a.clone(),
85 ..ChannelEnd::default()
86 };
87
88 state.put_channel(&channel_id, &self.port_id_on_b, new_channel.clone());
89 state.put_send_sequence(&channel_id, &self.port_id_on_b, 1);
90 state.put_recv_sequence(&channel_id, &self.port_id_on_b, 1);
91 state.put_ack_sequence(&channel_id, &self.port_id_on_b, 1);
92
93 state.record(
94 events::channel::OpenTry {
95 port_id: self.port_id_on_b.clone(),
96 channel_id: channel_id.clone(),
97 counterparty_port_id: new_channel.counterparty().port_id().clone(),
98 counterparty_channel_id: new_channel
99 .counterparty()
100 .channel_id
101 .clone()
102 .unwrap_or_default(),
103 connection_id: new_channel.connection_hops[0].clone(),
104 version: new_channel.version.clone(),
105 }
106 .into(),
107 );
108
109 let transfer = PortId::transfer();
110 if self.port_id_on_b == transfer {
111 AH::chan_open_try_execute(state, self).await;
112 } else {
113 anyhow::bail!("invalid port id");
114 }
115
116 Ok(())
117 }
118}
119
120pub fn connection_hops_eq_1(msg: &MsgChannelOpenTry) -> anyhow::Result<()> {
121 if msg.connection_hops_on_b.len() != 1 {
122 anyhow::bail!("currently only channels with one connection hop are supported");
123 }
124 Ok(())
125}
126
127async fn verify_connections_open<S: StateRead>(
128 state: S,
129 msg: &MsgChannelOpenTry,
130) -> anyhow::Result<ConnectionEnd> {
131 let connection = state
132 .get_connection(&msg.connection_hops_on_b[0])
133 .await?
134 .ok_or_else(|| anyhow::anyhow!("connection not found"))?;
135
136 if connection.state != ConnectionState::Open {
137 Err(anyhow::anyhow!("connection for channel is not open"))
138 } else {
139 Ok(connection)
140 }
141}