penumbra_ibc/component/msg_handler/
connection_open_init.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use anyhow::{Context, Result};
use async_trait::async_trait;
use ibc_types::core::connection::{
    events, msgs::MsgConnectionOpenInit, ConnectionEnd, ConnectionId, Version,
};

use crate::component::{
    client::StateReadExt as _,
    connection::{StateReadExt as _, StateWriteExt as _},
    connection_counter::SUPPORTED_VERSIONS,
    MsgHandler,
};

use cnidarium::StateWrite;
use ibc_types::core::connection::State as ConnectionState;

#[async_trait]
impl MsgHandler for MsgConnectionOpenInit {
    async fn check_stateless<H>(&self) -> Result<()> {
        version_is_supported(self)?;

        Ok(())
    }

    async fn try_execute<S: StateWrite, AH, HI>(&self, mut state: S) -> Result<()> {
        tracing::debug!(msg = ?self);

        // check that the client with the specified ID exists
        state.get_client_state(&self.client_id_on_a).await?;
        state.get_client_type(&self.client_id_on_a).await?;

        let connection_id = ConnectionId::new(
            state
                .get_connection_counter()
                .await
                .context("unable to get connection counter")?
                .0,
        );

        let compatible_versions = vec![Version::default()];

        let new_connection_end = ConnectionEnd {
            state: ConnectionState::Init,
            client_id: self.client_id_on_a.clone(),
            counterparty: self.counterparty.clone(),
            versions: compatible_versions,
            delay_period: self.delay_period,
        };

        // commit the connection, this also increments the connection counter
        state
            .put_new_connection(&connection_id, new_connection_end)
            .await
            .context("unable to put new connection")?;

        state.record(
            events::ConnectionOpenInit {
                connection_id: connection_id.clone(),
                client_id_on_a: self.client_id_on_a.clone(),
                client_id_on_b: self.counterparty.client_id.clone(),
            }
            .into(),
        );

        Ok(())
    }
}

fn version_is_supported(msg: &MsgConnectionOpenInit) -> anyhow::Result<()> {
    // check if the version is supported (we use the same versions as the cosmos SDK)
    // TODO: should we be storing the compatible versions in our state instead?

    // NOTE: version can be nil in MsgConnectionOpenInit
    if msg.version.is_none() {
        return Ok(());
    }

    if !SUPPORTED_VERSIONS.contains(
        msg.version
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("invalid version"))?,
    ) {
        Err(anyhow::anyhow!(
            "unsupported version: in ConnectionOpenInit",
        ))
    } else {
        Ok(())
    }
}