ibc_core_channel/handler/
timeout.rs1use ibc_core_channel_types::channel::{Counterparty, Order, State};
2use ibc_core_channel_types::commitment::compute_packet_commitment;
3use ibc_core_channel_types::error::ChannelError;
4use ibc_core_channel_types::events::{ChannelClosed, TimeoutPacket};
5use ibc_core_channel_types::msgs::{MsgTimeout, MsgTimeoutOnClose};
6use ibc_core_client::context::prelude::*;
7use ibc_core_connection::delay::verify_conn_delay_passed;
8use ibc_core_handler_types::events::{IbcEvent, MessageEvent};
9use ibc_core_host::types::path::{
10 ChannelEndPath, ClientConsensusStatePath, CommitmentPath, Path, ReceiptPath, SeqRecvPath,
11};
12use ibc_core_host::{ExecutionContext, ValidationContext};
13use ibc_core_router::module::Module;
14use ibc_primitives::prelude::*;
15
16use super::timeout_on_close;
17
18pub enum TimeoutMsgType {
19 Timeout(MsgTimeout),
20 TimeoutOnClose(MsgTimeoutOnClose),
21}
22
23pub fn timeout_packet_validate<ValCtx>(
24 ctx_a: &ValCtx,
25 module: &dyn Module,
26 timeout_msg_type: TimeoutMsgType,
27) -> Result<(), ChannelError>
28where
29 ValCtx: ValidationContext,
30{
31 match &timeout_msg_type {
32 TimeoutMsgType::Timeout(msg) => validate(ctx_a, msg),
33 TimeoutMsgType::TimeoutOnClose(msg) => timeout_on_close::validate(ctx_a, msg),
34 }?;
35
36 let (packet, signer) = match timeout_msg_type {
37 TimeoutMsgType::Timeout(msg) => (msg.packet, msg.signer),
38 TimeoutMsgType::TimeoutOnClose(msg) => (msg.packet, msg.signer),
39 };
40
41 module.on_timeout_packet_validate(&packet, &signer)
42}
43
44pub fn timeout_packet_execute<ExecCtx>(
45 ctx_a: &mut ExecCtx,
46 module: &mut dyn Module,
47 timeout_msg_type: TimeoutMsgType,
48) -> Result<(), ChannelError>
49where
50 ExecCtx: ExecutionContext,
51{
52 let (packet, signer) = match timeout_msg_type {
53 TimeoutMsgType::Timeout(msg) => (msg.packet, msg.signer),
54 TimeoutMsgType::TimeoutOnClose(msg) => (msg.packet, msg.signer),
55 };
56 let chan_end_path_on_a = ChannelEndPath::new(&packet.port_id_on_a, &packet.chan_id_on_a);
57 let chan_end_on_a = ctx_a.channel_end(&chan_end_path_on_a)?;
58
59 let event = IbcEvent::TimeoutPacket(TimeoutPacket::new(packet.clone(), chan_end_on_a.ordering));
61 ctx_a.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?;
62 ctx_a.emit_ibc_event(event)?;
63
64 let commitment_path_on_a =
65 CommitmentPath::new(&packet.port_id_on_a, &packet.chan_id_on_a, packet.seq_on_a);
66
67 if ctx_a.get_packet_commitment(&commitment_path_on_a).is_err() {
69 return Ok(());
74 };
75
76 let (extras, cb_result) = module.on_timeout_packet_execute(&packet, &signer);
77
78 cb_result?;
79
80 let chan_end_on_a = {
82 ctx_a.delete_packet_commitment(&commitment_path_on_a)?;
83
84 if let Order::Ordered = chan_end_on_a.ordering {
85 let mut chan_end_on_a = chan_end_on_a;
86 chan_end_on_a.state = State::Closed;
87 ctx_a.store_channel(&chan_end_path_on_a, chan_end_on_a.clone())?;
88
89 chan_end_on_a
90 } else {
91 chan_end_on_a
92 }
93 };
94
95 {
97 ctx_a.log_message("success: packet timeout".to_string())?;
98
99 if let Order::Ordered = chan_end_on_a.ordering {
100 let conn_id_on_a = chan_end_on_a.connection_hops()[0].clone();
101
102 let event = IbcEvent::ChannelClosed(ChannelClosed::new(
103 packet.port_id_on_a.clone(),
104 packet.chan_id_on_a.clone(),
105 chan_end_on_a.counterparty().port_id.clone(),
106 chan_end_on_a.counterparty().channel_id.clone(),
107 conn_id_on_a,
108 chan_end_on_a.ordering,
109 ));
110 ctx_a.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?;
111 ctx_a.emit_ibc_event(event)?;
112 }
113
114 for module_event in extras.events {
115 ctx_a.emit_ibc_event(IbcEvent::Module(module_event))?;
116 }
117
118 for log_message in extras.log {
119 ctx_a.log_message(log_message)?;
120 }
121 }
122
123 Ok(())
124}
125
126fn validate<Ctx>(ctx_a: &Ctx, msg: &MsgTimeout) -> Result<(), ChannelError>
127where
128 Ctx: ValidationContext,
129{
130 ctx_a.validate_message_signer(&msg.signer)?;
131
132 let chan_end_on_a = ctx_a.channel_end(&ChannelEndPath::new(
133 &msg.packet.port_id_on_a,
134 &msg.packet.chan_id_on_a,
135 ))?;
136
137 chan_end_on_a.verify_state_matches(&State::Open)?;
138
139 let counterparty = Counterparty::new(
140 msg.packet.port_id_on_b.clone(),
141 Some(msg.packet.chan_id_on_b.clone()),
142 );
143
144 chan_end_on_a.verify_counterparty_matches(&counterparty)?;
145
146 let conn_id_on_a = chan_end_on_a.connection_hops()[0].clone();
147 let conn_end_on_a = ctx_a.connection_end(&conn_id_on_a)?;
148
149 let commitment_path_on_a = CommitmentPath::new(
151 &msg.packet.port_id_on_a,
152 &msg.packet.chan_id_on_a,
153 msg.packet.seq_on_a,
154 );
155 let Ok(commitment_on_a) = ctx_a.get_packet_commitment(&commitment_path_on_a) else {
156 return Ok(());
161 };
162
163 let expected_commitment_on_a = compute_packet_commitment(
164 &msg.packet.data,
165 &msg.packet.timeout_height_on_b,
166 &msg.packet.timeout_timestamp_on_b,
167 );
168
169 if commitment_on_a != expected_commitment_on_a {
170 return Err(ChannelError::MismatchedPacketCommitment {
171 expected: expected_commitment_on_a,
172 actual: commitment_on_a,
173 });
174 }
175
176 {
178 let client_id_on_a = conn_end_on_a.client_id();
179 let client_val_ctx_a = ctx_a.get_client_validation_context();
180 let client_state_of_b_on_a = client_val_ctx_a.client_state(client_id_on_a)?;
181
182 client_state_of_b_on_a
183 .status(ctx_a.get_client_validation_context(), client_id_on_a)?
184 .verify_is_active()?;
185
186 client_state_of_b_on_a.validate_proof_height(msg.proof_height_on_b)?;
187
188 let client_cons_state_path_on_a = ClientConsensusStatePath::new(
190 client_id_on_a.clone(),
191 msg.proof_height_on_b.revision_number(),
192 msg.proof_height_on_b.revision_height(),
193 );
194 let consensus_state_of_b_on_a =
195 client_val_ctx_a.consensus_state(&client_cons_state_path_on_a)?;
196 let timestamp_of_b = consensus_state_of_b_on_a.timestamp()?;
197
198 if !msg.packet.timed_out(×tamp_of_b, msg.proof_height_on_b) {
199 return Err(ChannelError::InsufficientPacketTimeout {
200 timeout_height: msg.packet.timeout_height_on_b,
201 chain_height: msg.proof_height_on_b,
202 timeout_timestamp: msg.packet.timeout_timestamp_on_b,
203 chain_timestamp: timestamp_of_b,
204 });
205 }
206
207 verify_conn_delay_passed(ctx_a, msg.proof_height_on_b, &conn_end_on_a)?;
208
209 let next_seq_recv_verification_result = match chan_end_on_a.ordering {
210 Order::Ordered => {
211 if msg.packet.seq_on_a < msg.next_seq_recv_on_b {
212 return Err(ChannelError::MismatchedPacketSequence {
213 actual: msg.packet.seq_on_a,
214 expected: msg.next_seq_recv_on_b,
215 });
216 }
217 let seq_recv_path_on_b =
218 SeqRecvPath::new(&msg.packet.port_id_on_b, &msg.packet.chan_id_on_b);
219
220 client_state_of_b_on_a.verify_membership(
221 conn_end_on_a.counterparty().prefix(),
222 &msg.proof_unreceived_on_b,
223 consensus_state_of_b_on_a.root(),
224 Path::SeqRecv(seq_recv_path_on_b),
225 msg.packet.seq_on_a.to_vec(),
226 )
227 }
228 Order::Unordered => {
229 let receipt_path_on_b = ReceiptPath::new(
230 &msg.packet.port_id_on_b,
231 &msg.packet.chan_id_on_b,
232 msg.packet.seq_on_a,
233 );
234
235 client_state_of_b_on_a.verify_non_membership(
236 conn_end_on_a.counterparty().prefix(),
237 &msg.proof_unreceived_on_b,
238 consensus_state_of_b_on_a.root(),
239 Path::Receipt(receipt_path_on_b),
240 )
241 }
242 Order::None => {
243 return Err(ChannelError::InvalidState {
244 expected: "Channel ordering to not be None".to_string(),
245 actual: chan_end_on_a.ordering.to_string(),
246 })
247 }
248 };
249
250 next_seq_recv_verification_result?;
251 }
252
253 Ok(())
254}