ibc_core_channel/handler/
timeout_on_close.rs

1use 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    //verify the packet was sent, check the store
39    let Ok(commitment_on_a) = ctx_a.get_packet_commitment(&commitment_path_on_a) else {
40        // This error indicates that the timeout has already been relayed
41        // or there is a misconfigured relayer attempting to prove a timeout
42        // for a packet never sent. Core IBC will treat this error as a no-op in order to
43        // prevent an entire relay transaction from failing and consuming unnecessary fees.
44        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    // Verify proofs
63    {
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        // Verify the proof for the channel state against the expected channel end.
107        // A counterparty channel id of None in not possible, and is checked by validate_basic in msg.
108        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}