penumbra_ibc/component/msg_handler/
channel_open_ack.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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use anyhow::Result;
use async_trait::async_trait;
use cnidarium::{StateRead, StateWrite};
use ibc_types::core::{
    channel::channel::State as ChannelState, channel::events, channel::msgs::MsgChannelOpenAck,
    channel::ChannelEnd, channel::Counterparty, channel::PortId, connection::ConnectionEnd,
    connection::State as ConnectionState,
};

use crate::component::{
    app_handler::{AppHandlerCheck, AppHandlerExecute},
    channel::{StateReadExt as _, StateWriteExt as _},
    connection::StateReadExt as _,
    proof_verification::ChannelProofVerifier,
    HostInterface, MsgHandler,
};

#[async_trait]
impl MsgHandler for MsgChannelOpenAck {
    async fn check_stateless<H: AppHandlerCheck>(&self) -> Result<()> {
        // NOTE: no additional stateless validation is possible

        Ok(())
    }

    async fn try_execute<
        S: StateWrite,
        AH: AppHandlerCheck + AppHandlerExecute,
        HI: HostInterface,
    >(
        &self,
        mut state: S,
    ) -> Result<()> {
        tracing::debug!(msg = ?self);
        let mut channel = state
            .get_channel(&self.chan_id_on_a, &self.port_id_on_a)
            .await?
            .ok_or_else(|| anyhow::anyhow!("channel not found"))?;

        channel_state_is_correct(&channel)?;

        // TODO: capability authentication?

        let connection = verify_channel_connection_open(&state, &channel).await?;

        let expected_counterparty =
            Counterparty::new(self.port_id_on_a.clone(), Some(self.chan_id_on_a.clone()));

        let expected_connection_hops = vec![connection
            .counterparty
            .connection_id
            .clone()
            .ok_or_else(|| anyhow::anyhow!("no counterparty connection id provided"))?];

        let expected_channel = ChannelEnd {
            state: ChannelState::TryOpen,
            ordering: channel.ordering,
            remote: expected_counterparty,
            connection_hops: expected_connection_hops,
            version: self.version_on_b.clone(),
        };

        state
            .verify_channel_proof(
                &connection,
                &self.proof_chan_end_on_b,
                &self.proof_height_on_b,
                &self.chan_id_on_b,
                &channel.remote.port_id,
                &expected_channel,
            )
            .await?;

        let transfer = PortId::transfer();
        if self.port_id_on_a == transfer {
            AH::chan_open_ack_check(&mut state, self).await?;
        } else {
            anyhow::bail!("invalid port id");
        }

        channel.set_state(ChannelState::Open);
        channel.set_version(self.version_on_b.clone());
        channel.set_counterparty_channel_id(self.chan_id_on_b.clone());
        state.put_channel(&self.chan_id_on_a, &self.port_id_on_a, channel.clone());

        state.record(
            events::channel::OpenAck {
                port_id: self.port_id_on_a.clone(),
                channel_id: self.chan_id_on_a.clone(),
                counterparty_channel_id: channel
                    .counterparty()
                    .channel_id
                    .clone()
                    .unwrap_or_default(),
                counterparty_port_id: channel.counterparty().port_id.clone(),
                connection_id: channel.connection_hops[0].clone(),
            }
            .into(),
        );

        let transfer = PortId::transfer();
        if self.port_id_on_a == transfer {
            AH::chan_open_ack_execute(state, self).await;
        } else {
            anyhow::bail!("invalid port id");
        }

        Ok(())
    }
}

fn channel_state_is_correct(channel: &ChannelEnd) -> anyhow::Result<()> {
    if channel.state == ChannelState::Init {
        Ok(())
    } else {
        Err(anyhow::anyhow!("channel is not in the correct state"))
    }
}

async fn verify_channel_connection_open<S: StateRead>(
    state: S,
    channel: &ChannelEnd,
) -> anyhow::Result<ConnectionEnd> {
    let connection = state
        .get_connection(&channel.connection_hops[0])
        .await?
        .ok_or_else(|| anyhow::anyhow!("connection not found for channel"))?;

    if connection.state != ConnectionState::Open {
        Err(anyhow::anyhow!("connection for channel is not open"))
    } else {
        Ok(connection)
    }
}