penumbra_sdk_ibc/component/msg_handler/
channel_close_confirm.rs1use anyhow::Result;
2use async_trait::async_trait;
3use cnidarium::StateWrite;
4use ibc_types::core::{
5 channel::{
6 channel::State as ChannelState, events, msgs::MsgChannelCloseConfirm, ChannelEnd,
7 Counterparty, PortId,
8 },
9 connection::State as ConnectionState,
10};
11
12use crate::component::{
13 app_handler::{AppHandlerCheck, AppHandlerExecute},
14 channel::{StateReadExt as _, StateWriteExt as _},
15 connection::StateReadExt as _,
16 proof_verification::ChannelProofVerifier,
17 HostInterface, MsgHandler,
18};
19
20#[async_trait]
21impl MsgHandler for MsgChannelCloseConfirm {
22 async fn check_stateless<AH>(&self) -> Result<()> {
23 Ok(())
26 }
27
28 async fn try_execute<
29 S: StateWrite,
30 AH: AppHandlerCheck + AppHandlerExecute,
31 HI: HostInterface,
32 >(
33 &self,
34 mut state: S,
35 ) -> Result<()> {
36 tracing::debug!(msg = ?self);
37 let mut channel = state
43 .get_channel(&self.chan_id_on_b, &self.port_id_on_b)
44 .await?
45 .ok_or_else(|| anyhow::anyhow!("channel not found"))?;
46 if channel.state_matches(&ChannelState::Closed) {
47 anyhow::bail!("channel is already closed");
48 }
49
50 let connection = state
51 .get_connection(&channel.connection_hops[0])
52 .await?
53 .ok_or_else(|| anyhow::anyhow!("connection not found for channel"))?;
54 if !connection.state_matches(&ConnectionState::Open) {
55 anyhow::bail!("connection for channel is not open");
56 }
57
58 let expected_connection_hops = vec![connection
59 .counterparty
60 .connection_id
61 .clone()
62 .ok_or_else(|| anyhow::anyhow!("no counterparty connection id provided"))?];
63
64 let expected_counterparty =
65 Counterparty::new(self.port_id_on_b.clone(), Some(self.chan_id_on_b.clone()));
66
67 let expected_channel = ChannelEnd {
68 state: ChannelState::Closed,
69 ordering: channel.ordering,
70 remote: expected_counterparty,
71 connection_hops: expected_connection_hops,
72 version: channel.version.clone(),
73 ..ChannelEnd::default()
74 };
75
76 state
77 .verify_channel_proof(
78 &connection,
79 &self.proof_chan_end_on_a,
80 &self.proof_height_on_a,
81 &channel
82 .remote
83 .channel_id
84 .clone()
85 .ok_or_else(|| anyhow::anyhow!("no channel id"))?,
86 &channel.remote.port_id.clone(),
87 &expected_channel,
88 )
89 .await?;
90
91 let transfer = PortId::transfer();
92 if self.port_id_on_b == transfer {
93 AH::chan_close_confirm_check(&mut state, self).await?;
94 } else {
95 anyhow::bail!("invalid port id");
96 }
97 channel.set_state(ChannelState::Closed);
98 state.put_channel(&self.chan_id_on_b, &self.port_id_on_b, channel.clone());
99
100 state.record(
101 events::channel::CloseConfirm {
102 port_id: self.port_id_on_b.clone(),
103 channel_id: self.chan_id_on_b.clone(),
104 counterparty_port_id: channel.counterparty().port_id.clone(),
105 counterparty_channel_id: channel
106 .counterparty()
107 .channel_id
108 .clone()
109 .unwrap_or_default(),
110 connection_id: channel.connection_hops[0].clone(),
111 }
112 .into(),
113 );
114
115 let transfer = PortId::transfer();
117 if self.port_id_on_b == transfer {
118 AH::chan_close_confirm_execute(state, self).await;
119 } else {
120 anyhow::bail!("invalid port id");
121 }
122
123 Ok(())
124 }
125}