penumbra_sdk_ibc/component/msg_handler/
connection_open_init.rs

1use anyhow::{Context, Result};
2use async_trait::async_trait;
3use ibc_types::core::connection::{
4    events, msgs::MsgConnectionOpenInit, ConnectionEnd, ConnectionId, Version,
5};
6
7use crate::component::{
8    client::StateReadExt as _,
9    connection::{StateReadExt as _, StateWriteExt as _},
10    connection_counter::SUPPORTED_VERSIONS,
11    MsgHandler,
12};
13
14use cnidarium::StateWrite;
15use ibc_types::core::connection::State as ConnectionState;
16
17#[async_trait]
18impl MsgHandler for MsgConnectionOpenInit {
19    async fn check_stateless<H>(&self) -> Result<()> {
20        version_is_supported(self)?;
21
22        Ok(())
23    }
24
25    async fn try_execute<S: StateWrite, AH, HI>(&self, mut state: S) -> Result<()> {
26        tracing::debug!(msg = ?self);
27
28        // check that the client with the specified ID exists
29        state.get_client_state(&self.client_id_on_a).await?;
30        state.get_client_type(&self.client_id_on_a).await?;
31
32        let connection_id = ConnectionId::new(
33            state
34                .get_connection_counter()
35                .await
36                .context("unable to get connection counter")?
37                .0,
38        );
39
40        let compatible_versions = vec![Version::default()];
41
42        let new_connection_end = ConnectionEnd {
43            state: ConnectionState::Init,
44            client_id: self.client_id_on_a.clone(),
45            counterparty: self.counterparty.clone(),
46            versions: compatible_versions,
47            delay_period: self.delay_period,
48        };
49
50        // commit the connection, this also increments the connection counter
51        state
52            .put_new_connection(&connection_id, new_connection_end)
53            .await
54            .context("unable to put new connection")?;
55
56        state.record(
57            events::ConnectionOpenInit {
58                connection_id: connection_id.clone(),
59                client_id_on_a: self.client_id_on_a.clone(),
60                client_id_on_b: self.counterparty.client_id.clone(),
61            }
62            .into(),
63        );
64
65        Ok(())
66    }
67}
68
69fn version_is_supported(msg: &MsgConnectionOpenInit) -> anyhow::Result<()> {
70    // check if the version is supported (we use the same versions as the cosmos SDK)
71    // TODO: should we be storing the compatible versions in our state instead?
72
73    // NOTE: version can be nil in MsgConnectionOpenInit
74    if msg.version.is_none() {
75        return Ok(());
76    }
77
78    if !SUPPORTED_VERSIONS.contains(
79        msg.version
80            .as_ref()
81            .ok_or_else(|| anyhow::anyhow!("invalid version"))?,
82    ) {
83        Err(anyhow::anyhow!(
84            "unsupported version: in ConnectionOpenInit",
85        ))
86    } else {
87        Ok(())
88    }
89}