ibc_core_channel/handler/
timeout_on_close.rs1use ibc_core_channel_types::channel::{ChannelEnd, Counterparty, Order, State};
2use ibc_core_channel_types::commitment::compute_packet_commitment;
3use ibc_core_channel_types::error::ChannelError;
4use ibc_core_channel_types::msgs::MsgTimeoutOnClose;
5use ibc_core_client::context::prelude::*;
6use ibc_core_connection::delay::verify_conn_delay_passed;
7use ibc_core_connection::types::error::ConnectionError;
8use ibc_core_host::types::path::{
9 ChannelEndPath, ClientConsensusStatePath, CommitmentPath, Path, ReceiptPath, SeqRecvPath,
10};
11use ibc_core_host::ValidationContext;
12use ibc_primitives::prelude::*;
13use ibc_primitives::proto::Protobuf;
14
15pub fn validate<Ctx>(ctx_a: &Ctx, msg: &MsgTimeoutOnClose) -> Result<(), ChannelError>
16where
17 Ctx: ValidationContext,
18{
19 ctx_a.validate_message_signer(&msg.signer)?;
20
21 let packet = &msg.packet;
22 let chan_end_path_on_a = ChannelEndPath::new(&packet.port_id_on_a, &packet.chan_id_on_a);
23 let chan_end_on_a = ctx_a.channel_end(&chan_end_path_on_a)?;
24
25 let counterparty = Counterparty::new(
26 packet.port_id_on_b.clone(),
27 Some(packet.chan_id_on_b.clone()),
28 );
29
30 chan_end_on_a.verify_counterparty_matches(&counterparty)?;
31
32 let commitment_path_on_a = CommitmentPath::new(
33 &msg.packet.port_id_on_a,
34 &msg.packet.chan_id_on_a,
35 msg.packet.seq_on_a,
36 );
37
38 let Ok(commitment_on_a) = ctx_a.get_packet_commitment(&commitment_path_on_a) else {
40 return Ok(());
45 };
46
47 let expected_commitment_on_a = compute_packet_commitment(
48 &packet.data,
49 &packet.timeout_height_on_b,
50 &packet.timeout_timestamp_on_b,
51 );
52 if commitment_on_a != expected_commitment_on_a {
53 return Err(ChannelError::MismatchedPacketCommitment {
54 expected: expected_commitment_on_a,
55 actual: commitment_on_a,
56 });
57 }
58
59 let conn_id_on_a = chan_end_on_a.connection_hops()[0].clone();
60 let conn_end_on_a = ctx_a.connection_end(&conn_id_on_a)?;
61
62 {
64 let client_id_on_a = conn_end_on_a.client_id();
65 let client_val_ctx_a = ctx_a.get_client_validation_context();
66 let client_state_of_b_on_a = client_val_ctx_a.client_state(client_id_on_a)?;
67
68 client_state_of_b_on_a
69 .status(ctx_a.get_client_validation_context(), client_id_on_a)?
70 .verify_is_active()?;
71
72 client_state_of_b_on_a.validate_proof_height(msg.proof_height_on_b)?;
73
74 let client_cons_state_path_on_a = ClientConsensusStatePath::new(
75 client_id_on_a.clone(),
76 msg.proof_height_on_b.revision_number(),
77 msg.proof_height_on_b.revision_height(),
78 );
79 let consensus_state_of_b_on_a =
80 client_val_ctx_a.consensus_state(&client_cons_state_path_on_a)?;
81 let prefix_on_b = conn_end_on_a.counterparty().prefix();
82 let port_id_on_b = chan_end_on_a.counterparty().port_id.clone();
83 let chan_id_on_b = chan_end_on_a
84 .counterparty()
85 .channel_id()
86 .ok_or(ChannelError::MissingCounterparty)?;
87 let conn_id_on_b = conn_end_on_a
88 .counterparty()
89 .connection_id()
90 .ok_or(ConnectionError::MissingCounterparty)?;
91 let expected_conn_hops_on_b = vec![conn_id_on_b.clone()];
92 let expected_counterparty = Counterparty::new(
93 packet.port_id_on_a.clone(),
94 Some(packet.chan_id_on_a.clone()),
95 );
96 let expected_chan_end_on_b = ChannelEnd::new(
97 State::Closed,
98 *chan_end_on_a.ordering(),
99 expected_counterparty,
100 expected_conn_hops_on_b,
101 chan_end_on_a.version().clone(),
102 )?;
103
104 let chan_end_path_on_b = ChannelEndPath(port_id_on_b, chan_id_on_b.clone());
105
106 client_state_of_b_on_a.verify_membership(
109 prefix_on_b,
110 &msg.proof_close_on_b,
111 consensus_state_of_b_on_a.root(),
112 Path::ChannelEnd(chan_end_path_on_b),
113 expected_chan_end_on_b.encode_vec(),
114 )?;
115
116 verify_conn_delay_passed(ctx_a, msg.proof_height_on_b, &conn_end_on_a)?;
117
118 let next_seq_recv_verification_result = match chan_end_on_a.ordering {
119 Order::Ordered => {
120 if packet.seq_on_a < msg.next_seq_recv_on_b {
121 return Err(ChannelError::MismatchedPacketSequence {
122 actual: packet.seq_on_a,
123 expected: msg.next_seq_recv_on_b,
124 });
125 }
126 let seq_recv_path_on_b =
127 SeqRecvPath::new(&packet.port_id_on_b, &packet.chan_id_on_b);
128
129 client_state_of_b_on_a.verify_membership(
130 conn_end_on_a.counterparty().prefix(),
131 &msg.proof_unreceived_on_b,
132 consensus_state_of_b_on_a.root(),
133 Path::SeqRecv(seq_recv_path_on_b),
134 packet.seq_on_a.to_vec(),
135 )
136 }
137 Order::Unordered => {
138 let receipt_path_on_b = ReceiptPath::new(
139 &msg.packet.port_id_on_b,
140 &msg.packet.chan_id_on_b,
141 msg.packet.seq_on_a,
142 );
143
144 client_state_of_b_on_a.verify_non_membership(
145 conn_end_on_a.counterparty().prefix(),
146 &msg.proof_unreceived_on_b,
147 consensus_state_of_b_on_a.root(),
148 Path::Receipt(receipt_path_on_b),
149 )
150 }
151 Order::None => {
152 return Err(ChannelError::InvalidState {
153 expected: "Channel ordering to not be None".to_string(),
154 actual: chan_end_on_a.ordering.to_string(),
155 })
156 }
157 };
158
159 next_seq_recv_verification_result?;
160 };
161
162 Ok(())
163}