penumbra_sdk_ibc/component/msg_handler/
connection_open_confirm.rs1use anyhow::{Context, Result};
2use async_trait::async_trait;
3use cnidarium::{StateRead, StateWrite};
4use ibc_types::{
5 core::connection::{
6 events, msgs::MsgConnectionOpenConfirm, ConnectionEnd, Counterparty, State,
7 },
8 path::ConnectionPath,
9};
10
11use crate::{
12 component::{
13 client::StateReadExt as _,
14 connection::{StateReadExt as _, StateWriteExt as _},
15 proof_verification, MsgHandler,
16 },
17 IBC_COMMITMENT_PREFIX,
18};
19
20#[async_trait]
21impl MsgHandler for MsgConnectionOpenConfirm {
22 async fn check_stateless<H>(&self) -> Result<()> {
23 Ok(())
27 }
28
29 async fn try_execute<S: StateWrite, AH, HI>(&self, mut state: S) -> Result<()> {
30 tracing::debug!(msg = ?self);
31 let connection = verify_previous_connection(&state, self).await?;
45
46 let expected_conn = ConnectionEnd {
47 state: State::Open,
48 client_id: connection.counterparty.client_id.clone(),
49 counterparty: Counterparty {
50 client_id: connection.client_id.clone(),
51 connection_id: Some(self.conn_id_on_b.clone()),
52 prefix: IBC_COMMITMENT_PREFIX.clone(),
53 },
54 versions: connection.versions.to_vec(),
55 delay_period: connection.delay_period,
56 };
57
58 let trusted_client_state = state.get_client_state(&connection.client_id).await?;
60
61 if trusted_client_state.is_frozen() {
64 anyhow::bail!("client is frozen");
65 }
66
67 let trusted_consensus_state = state
69 .get_verified_consensus_state(&self.proof_height_on_a, &connection.client_id)
70 .await?;
71
72 let proof_conn_end_on_a = self.proof_conn_end_on_a.clone();
77 proof_verification::verify_connection_state(
78 &trusted_client_state,
79 self.proof_height_on_a,
80 &connection.counterparty.prefix,
81 &proof_conn_end_on_a,
82 &trusted_consensus_state.root,
83 &ConnectionPath::new(connection.counterparty.connection_id.as_ref().ok_or_else(
84 || anyhow::anyhow!("missing counterparty in connection open confirm"),
85 )?),
86 &expected_conn,
87 )?;
88
89 let mut connection = state
91 .get_connection(&self.conn_id_on_b)
92 .await?
93 .ok_or_else(|| anyhow::anyhow!("no connection with the given ID"))
94 .context("unable to get connection")?;
95
96 connection.state = State::Open;
97
98 state.update_connection(&self.conn_id_on_b, connection.clone());
99
100 state.record(
101 events::ConnectionOpenConfirm {
102 conn_id_on_b: self.conn_id_on_b.clone(),
103 client_id_on_b: connection.client_id.clone(),
104 conn_id_on_a: connection
105 .counterparty
106 .connection_id
107 .clone()
108 .unwrap_or_default(),
109 client_id_on_a: connection.counterparty.client_id,
110 }
111 .into(),
112 );
113
114 Ok(())
115 }
116}
117
118async fn verify_previous_connection<S: StateRead>(
119 state: S,
120 msg: &MsgConnectionOpenConfirm,
121) -> anyhow::Result<ConnectionEnd> {
122 let connection = state
123 .get_connection(&msg.conn_id_on_b)
124 .await?
125 .ok_or_else(|| anyhow::anyhow!("connection not found"))?;
126
127 if !connection.state_matches(&State::TryOpen) {
128 Err(anyhow::anyhow!("connection not in correct state"))
129 } else {
130 Ok(connection)
131 }
132}