penumbra_sdk_ibc/component/msg_handler/
timeout.rs1use anyhow::{Context, Result};
2use async_trait::async_trait;
3use cnidarium::StateWrite;
4use ibc_types::core::channel::{
5 channel::{Order as ChannelOrder, State as ChannelState},
6 events,
7 msgs::MsgTimeout,
8 PortId,
9};
10
11use crate::component::{
12 app_handler::{AppHandlerCheck, AppHandlerExecute},
13 channel::{StateReadExt as _, StateWriteExt},
14 client::StateReadExt,
15 connection::StateReadExt as _,
16 proof_verification::{commit_packet, PacketProofVerifier},
17 HostInterface, MsgHandler,
18};
19
20#[async_trait]
21impl MsgHandler for MsgTimeout {
22 async fn check_stateless<H: AppHandlerCheck>(&self) -> Result<()> {
23 Ok(())
26 }
27
28 async fn try_execute<
29 S: StateWrite,
30 H: AppHandlerCheck + AppHandlerExecute,
31 HI: HostInterface,
32 >(
33 &self,
34 mut state: S,
35 ) -> Result<()> {
36 tracing::debug!(msg = ?self);
37 let mut channel = state
38 .get_channel(&self.packet.chan_on_a, &self.packet.port_on_a)
39 .await
40 .context("failed to get channel")?
41 .ok_or_else(|| anyhow::anyhow!("channel not found"))?;
42 if !channel.state_matches(&ChannelState::Open) {
43 anyhow::bail!("channel is not open");
44 }
45
46 if self.packet.chan_on_b.ne(channel
48 .counterparty()
49 .channel_id()
50 .ok_or_else(|| anyhow::anyhow!("missing channel id"))?)
51 {
52 anyhow::bail!("packet destination channel does not match channel");
53 }
54 if self.packet.port_on_b != channel.counterparty().port_id {
55 anyhow::bail!("packet destination port does not match channel");
56 }
57
58 let connection = state
59 .get_connection(&channel.connection_hops[0])
60 .await
61 .context("failed to get connection")?
62 .ok_or_else(|| anyhow::anyhow!("connection not found for channel"))?;
63
64 let client_state = state.get_client_state(&connection.client_id).await?;
65 let last_consensus_state = state
66 .get_verified_consensus_state(&client_state.latest_height(), &connection.client_id)
67 .await?;
68 let last_update_time = last_consensus_state.timestamp;
69 let proof_update_height = self.proof_height_on_b;
70
71 if !self
73 .packet
74 .timed_out(&last_update_time.into(), proof_update_height)
75 {
76 anyhow::bail!("packet has not timed out on the counterparty chain");
77 }
78
79 let commitment = state
81 .get_packet_commitment(&self.packet)
82 .await
83 .context("failed to get packet commitment")?
84 .ok_or_else(|| anyhow::anyhow!("packet commitment not found"))?;
85 if commitment != commit_packet(&self.packet) {
86 anyhow::bail!("packet commitment does not match");
87 }
88
89 if channel.ordering == ChannelOrder::Ordered {
90 if self.next_seq_recv_on_b > self.packet.sequence {
92 anyhow::bail!("packet sequence number does not match");
93 }
94
95 state
98 .verify_packet_timeout_proof::<HI>(&connection, self)
99 .await
100 .context("failed to verify packet timeout proof")?;
101 } else {
102 state
105 .verify_packet_timeout_absence_proof::<HI>(&connection, self)
106 .await
107 .context("failed to verify packet timeout absence proof")?;
108 }
109
110 let transfer = PortId::transfer();
111 if self.packet.port_on_b == transfer {
112 H::timeout_packet_check(&mut state, self)
113 .await
114 .context("failed to execute handler for timeout_packet_check")?;
115 } else {
116 anyhow::bail!("invalid port id");
117 }
118
119 state.delete_packet_commitment(
120 &self.packet.chan_on_a,
121 &self.packet.port_on_a,
122 self.packet.sequence.into(),
123 );
124
125 if channel.ordering == ChannelOrder::Ordered {
126 channel.set_state(ChannelState::Closed);
128 state.put_channel(
129 &self.packet.chan_on_a,
130 &self.packet.port_on_a,
131 channel.clone(),
132 );
133 }
134
135 state.record(
136 events::packet::TimeoutPacket {
137 timeout_height: self.packet.timeout_height_on_b,
138 timeout_timestamp: self.packet.timeout_timestamp_on_b,
139 sequence: self.packet.sequence,
140 src_port_id: self.packet.port_on_a.clone(),
141 src_channel_id: self.packet.chan_on_a.clone(),
142 dst_port_id: self.packet.port_on_b.clone(),
143 dst_channel_id: self.packet.chan_on_b.clone(),
144 channel_ordering: channel.ordering,
145 }
146 .into(),
147 );
148
149 let transfer = PortId::transfer();
150 if self.packet.port_on_b == transfer {
151 H::timeout_packet_execute(state, self).await?;
152 } else {
153 anyhow::bail!("invalid port id");
154 }
155
156 Ok(())
157 }
158}