penumbra_sdk_ibc/component/msg_handler/
channel_open_init.rs1use anyhow::{Context, Result};
2use async_trait::async_trait;
3use cnidarium::{StateRead, StateWrite};
4use ibc_types::core::channel::msgs::MsgChannelOpenInit;
5use ibc_types::core::channel::{
6 channel::State, events, ChannelEnd, ChannelId, Counterparty, PortId,
7};
8
9use crate::component::HostInterface;
10use crate::component::{
11 app_handler::{AppHandlerCheck, AppHandlerExecute},
12 channel::{StateReadExt as _, StateWriteExt as _},
13 connection::StateReadExt as _,
14 MsgHandler,
15};
16
17#[async_trait]
18impl MsgHandler for MsgChannelOpenInit {
19 async fn check_stateless<H: AppHandlerCheck>(&self) -> Result<()> {
20 connection_hops_eq_1(self)?;
21
22 Ok(())
23 }
24
25 async fn try_execute<
26 S: StateWrite,
27 AH: AppHandlerCheck + AppHandlerExecute,
28 HI: HostInterface,
29 >(
30 &self,
31 mut state: S,
32 ) -> Result<()> {
33 tracing::debug!(msg = ?self);
34 let channel_id = get_channel_id(&state).await?;
35
36 verify_channel_does_not_exist(&state, &channel_id, &self.port_id_on_a).await?;
37
38 verify_connections_exist(&state, self).await?;
41
42 let transfer = PortId::transfer();
45 if self.port_id_on_a == transfer {
46 AH::chan_open_init_check(&mut state, self).await?;
47 } else {
48 anyhow::bail!("invalid port id");
49 }
50 let channel_id = state
51 .next_channel_id()
52 .await
53 .context("unable to get next channel id")?;
54 let new_channel = ChannelEnd {
55 state: State::Init,
56 ordering: self.ordering,
57 remote: Counterparty::new(self.port_id_on_b.clone(), None),
58 connection_hops: self.connection_hops_on_a.clone(),
59 version: self.version_proposal.clone(),
60 ..ChannelEnd::default()
61 };
62
63 state.put_channel(&channel_id, &self.port_id_on_a, new_channel.clone());
64 state.put_send_sequence(&channel_id, &self.port_id_on_a, 1);
65 state.put_recv_sequence(&channel_id, &self.port_id_on_a, 1);
66 state.put_ack_sequence(&channel_id, &self.port_id_on_a, 1);
67
68 state.record(
69 events::channel::OpenInit {
70 port_id: self.port_id_on_a.clone(),
71 channel_id: channel_id.clone(),
72 counterparty_port_id: new_channel.counterparty().port_id().clone(),
73 connection_id: new_channel.connection_hops[0].clone(),
74 version: new_channel.version.clone(),
75 }
76 .into(),
77 );
78
79 let transfer = PortId::transfer();
80 if self.port_id_on_a == transfer {
81 AH::chan_open_init_execute(state, self).await;
82 } else {
83 anyhow::bail!("invalid port id");
84 }
85
86 Ok(())
87 }
88}
89
90fn connection_hops_eq_1(msg: &MsgChannelOpenInit) -> anyhow::Result<()> {
91 if msg.connection_hops_on_a.len() != 1 {
92 anyhow::bail!("currently only channels with one connection hop are supported");
93 }
94 Ok(())
95}
96async fn verify_connections_exist<S: StateRead>(
97 state: S,
98 msg: &MsgChannelOpenInit,
99) -> anyhow::Result<()> {
100 state
101 .get_connection(&msg.connection_hops_on_a[0])
102 .await?
103 .ok_or_else(|| anyhow::anyhow!("connection not found"))
104 .map(|_| ())
105}
106
107async fn get_channel_id<S: StateRead>(state: S) -> anyhow::Result<ChannelId> {
108 let counter = state.get_channel_counter().await?;
109
110 Ok(ChannelId::new(counter))
111}
112
113async fn verify_channel_does_not_exist<S: StateRead>(
114 state: S,
115 channel_id: &ChannelId,
116 port_id: &PortId,
117) -> anyhow::Result<()> {
118 let channel = state.get_channel(channel_id, port_id).await?;
119 if channel.is_some() {
120 anyhow::bail!("channel already exists");
121 }
122 Ok(())
123}