ibc_core_channel/handler/
acknowledgement.rs

1use ibc_core_channel_types::acknowledgement::Acknowledgement;
2use ibc_core_channel_types::channel::{ChannelEnd, Counterparty, Order, State as ChannelState};
3use ibc_core_channel_types::commitment::{compute_ack_commitment, compute_packet_commitment};
4use ibc_core_channel_types::error::ChannelError;
5use ibc_core_channel_types::events::{AcknowledgePacket, WriteAcknowledgement};
6use ibc_core_channel_types::msgs::MsgAcknowledgement;
7use ibc_core_channel_types::packet::{Packet, Receipt};
8use ibc_core_client::context::prelude::*;
9use ibc_core_connection::delay::verify_conn_delay_passed;
10use ibc_core_connection::types::State as ConnectionState;
11use ibc_core_handler_types::events::{IbcEvent, MessageEvent};
12use ibc_core_host::types::path::{
13    AckPath, ChannelEndPath, ClientConsensusStatePath, CommitmentPath, Path, ReceiptPath,
14    SeqAckPath, SeqRecvPath,
15};
16use ibc_core_host::{ExecutionContext, ValidationContext};
17use ibc_core_router::module::Module;
18use ibc_primitives::prelude::*;
19
20pub fn commit_packet_sequence_number_with_chan_end<ExecCtx>(
21    ctx_b: &mut ExecCtx,
22    chan_end_on_b: &ChannelEnd,
23    packet: &Packet,
24) -> Result<(), ChannelError>
25where
26    ExecCtx: ExecutionContext,
27{
28    // `recvPacket` core handler state changes
29    match chan_end_on_b.ordering {
30        Order::Unordered => {
31            let receipt_path_on_b = ReceiptPath {
32                port_id: packet.port_id_on_b.clone(),
33                channel_id: packet.chan_id_on_b.clone(),
34                sequence: packet.seq_on_a,
35            };
36
37            ctx_b.store_packet_receipt(&receipt_path_on_b, Receipt::Ok)?;
38        }
39        Order::Ordered => {
40            let seq_recv_path_on_b = SeqRecvPath::new(&packet.port_id_on_b, &packet.chan_id_on_b);
41            let next_seq_recv = ctx_b.get_next_sequence_recv(&seq_recv_path_on_b)?;
42            ctx_b.store_next_sequence_recv(&seq_recv_path_on_b, next_seq_recv.increment())?;
43        }
44        _ => {}
45    }
46
47    Ok(())
48}
49
50pub fn commit_packet_sequence_number<ExecCtx>(
51    ctx_b: &mut ExecCtx,
52    packet: &Packet,
53) -> Result<(), ChannelError>
54where
55    ExecCtx: ExecutionContext,
56{
57    let chan_end_path_on_b = ChannelEndPath::new(&packet.port_id_on_b, &packet.chan_id_on_b);
58    let chan_end_on_b = ctx_b.channel_end(&chan_end_path_on_b)?;
59
60    commit_packet_sequence_number_with_chan_end(ctx_b, &chan_end_on_b, packet)
61}
62
63pub fn commit_packet_acknowledgment<ExecCtx>(
64    ctx_b: &mut ExecCtx,
65    packet: &Packet,
66    acknowledgement: &Acknowledgement,
67) -> Result<(), ChannelError>
68where
69    ExecCtx: ExecutionContext,
70{
71    let ack_path_on_b = AckPath::new(&packet.port_id_on_b, &packet.chan_id_on_b, packet.seq_on_a);
72
73    // `writeAcknowledgement` handler state changes
74    ctx_b.store_packet_acknowledgement(&ack_path_on_b, compute_ack_commitment(acknowledgement))?;
75
76    Ok(())
77}
78
79pub fn emit_packet_acknowledgement_event_with_chan_end<ExecCtx>(
80    ctx_b: &mut ExecCtx,
81    chan_end_on_b: &ChannelEnd,
82    packet: Packet,
83    acknowledgement: Acknowledgement,
84) -> Result<(), ChannelError>
85where
86    ExecCtx: ExecutionContext,
87{
88    let conn_id_on_b = &chan_end_on_b.connection_hops()[0];
89
90    ctx_b.log_message("success: packet write acknowledgement".to_string())?;
91
92    let event = IbcEvent::WriteAcknowledgement(WriteAcknowledgement::new(
93        packet,
94        acknowledgement,
95        conn_id_on_b.clone(),
96    ));
97    ctx_b.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?;
98    ctx_b.emit_ibc_event(event)?;
99
100    Ok(())
101}
102
103pub fn emit_packet_acknowledgement_event<ExecCtx>(
104    ctx_b: &mut ExecCtx,
105    packet: Packet,
106    acknowledgement: Acknowledgement,
107) -> Result<(), ChannelError>
108where
109    ExecCtx: ExecutionContext,
110{
111    let chan_end_path_on_b = ChannelEndPath::new(&packet.port_id_on_b, &packet.chan_id_on_b);
112    let chan_end_on_b = ctx_b.channel_end(&chan_end_path_on_b)?;
113
114    emit_packet_acknowledgement_event_with_chan_end(ctx_b, &chan_end_on_b, packet, acknowledgement)
115}
116
117pub fn acknowledgement_packet_validate<ValCtx>(
118    ctx_a: &ValCtx,
119    module: &dyn Module,
120    msg: MsgAcknowledgement,
121) -> Result<(), ChannelError>
122where
123    ValCtx: ValidationContext,
124{
125    validate(ctx_a, &msg)?;
126
127    module.on_acknowledgement_packet_validate(&msg.packet, &msg.acknowledgement, &msg.signer)
128}
129
130pub fn acknowledgement_packet_execute<ExecCtx>(
131    ctx_a: &mut ExecCtx,
132    module: &mut dyn Module,
133    msg: MsgAcknowledgement,
134) -> Result<(), ChannelError>
135where
136    ExecCtx: ExecutionContext,
137{
138    let chan_end_path_on_a =
139        ChannelEndPath::new(&msg.packet.port_id_on_a, &msg.packet.chan_id_on_a);
140    let chan_end_on_a = ctx_a.channel_end(&chan_end_path_on_a)?;
141    let conn_id_on_a = &chan_end_on_a.connection_hops()[0];
142
143    // In all cases, this event is emitted
144    let event = IbcEvent::AcknowledgePacket(AcknowledgePacket::new(
145        msg.packet.clone(),
146        chan_end_on_a.ordering,
147        conn_id_on_a.clone(),
148    ));
149    ctx_a.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?;
150    ctx_a.emit_ibc_event(event)?;
151
152    let commitment_path_on_a = CommitmentPath::new(
153        &msg.packet.port_id_on_a,
154        &msg.packet.chan_id_on_a,
155        msg.packet.seq_on_a,
156    );
157
158    // check if we're in the NO-OP case
159    if ctx_a.get_packet_commitment(&commitment_path_on_a).is_err() {
160        // This error indicates that the timeout has already been relayed
161        // or there is a misconfigured relayer attempting to prove a timeout
162        // for a packet never sent. Core IBC will treat this error as a no-op in order to
163        // prevent an entire relay transaction from failing and consuming unnecessary fees.
164        return Ok(());
165    };
166
167    let (extras, cb_result) =
168        module.on_acknowledgement_packet_execute(&msg.packet, &msg.acknowledgement, &msg.signer);
169
170    cb_result?;
171
172    // apply state changes
173    {
174        ctx_a.delete_packet_commitment(&commitment_path_on_a)?;
175
176        if let Order::Ordered = chan_end_on_a.ordering {
177            // Note: in validation, we verified that `msg.packet.sequence == nextSeqRecv`
178            // (where `nextSeqRecv` is the value in the store)
179            let seq_ack_path_on_a =
180                SeqAckPath::new(&msg.packet.port_id_on_a, &msg.packet.chan_id_on_a);
181            ctx_a.store_next_sequence_ack(&seq_ack_path_on_a, msg.packet.seq_on_a.increment())?;
182        }
183    }
184
185    // emit events and logs
186    {
187        ctx_a.log_message("success: packet acknowledgement".to_string())?;
188
189        // Note: Acknowledgement event was emitted at the beginning
190
191        for module_event in extras.events {
192            ctx_a.emit_ibc_event(IbcEvent::Module(module_event))?
193        }
194
195        for log_message in extras.log {
196            ctx_a.log_message(log_message)?;
197        }
198    }
199
200    Ok(())
201}
202
203fn validate<Ctx>(ctx_a: &Ctx, msg: &MsgAcknowledgement) -> Result<(), ChannelError>
204where
205    Ctx: ValidationContext,
206{
207    ctx_a.validate_message_signer(&msg.signer)?;
208
209    let packet = &msg.packet;
210    let chan_end_path_on_a = ChannelEndPath::new(&packet.port_id_on_a, &packet.chan_id_on_a);
211    let chan_end_on_a = ctx_a.channel_end(&chan_end_path_on_a)?;
212
213    chan_end_on_a.verify_state_matches(&ChannelState::Open)?;
214
215    let counterparty = Counterparty::new(
216        packet.port_id_on_b.clone(),
217        Some(packet.chan_id_on_b.clone()),
218    );
219
220    chan_end_on_a.verify_counterparty_matches(&counterparty)?;
221
222    let conn_id_on_a = &chan_end_on_a.connection_hops()[0];
223    let conn_end_on_a = ctx_a.connection_end(conn_id_on_a)?;
224
225    conn_end_on_a.verify_state_matches(&ConnectionState::Open)?;
226
227    let commitment_path_on_a =
228        CommitmentPath::new(&packet.port_id_on_a, &packet.chan_id_on_a, packet.seq_on_a);
229
230    // Verify packet commitment
231    let Ok(commitment_on_a) = ctx_a.get_packet_commitment(&commitment_path_on_a) else {
232        // This error indicates that the timeout has already been relayed
233        // or there is a misconfigured relayer attempting to prove a timeout
234        // for a packet never sent. Core IBC will treat this error as a no-op in order to
235        // prevent an entire relay transaction from failing and consuming unnecessary fees.
236        return Ok(());
237    };
238
239    let expected_commitment_on_a = compute_packet_commitment(
240        &packet.data,
241        &packet.timeout_height_on_b,
242        &packet.timeout_timestamp_on_b,
243    );
244
245    if commitment_on_a != expected_commitment_on_a {
246        return Err(ChannelError::MismatchedPacketCommitment {
247            actual: commitment_on_a,
248            expected: expected_commitment_on_a,
249        });
250    }
251
252    if let Order::Ordered = chan_end_on_a.ordering {
253        let seq_ack_path_on_a = SeqAckPath::new(&packet.port_id_on_a, &packet.chan_id_on_a);
254        let next_seq_ack = ctx_a.get_next_sequence_ack(&seq_ack_path_on_a)?;
255        if packet.seq_on_a != next_seq_ack {
256            return Err(ChannelError::MismatchedPacketSequence {
257                actual: packet.seq_on_a,
258                expected: next_seq_ack,
259            });
260        }
261    }
262
263    // Verify proofs
264    {
265        let client_id_on_a = conn_end_on_a.client_id();
266
267        let client_val_ctx_a = ctx_a.get_client_validation_context();
268
269        let client_state_of_b_on_a = client_val_ctx_a.client_state(client_id_on_a)?;
270
271        client_state_of_b_on_a
272            .status(ctx_a.get_client_validation_context(), client_id_on_a)?
273            .verify_is_active()?;
274
275        client_state_of_b_on_a.validate_proof_height(msg.proof_height_on_b)?;
276
277        let client_cons_state_path_on_a = ClientConsensusStatePath::new(
278            client_id_on_a.clone(),
279            msg.proof_height_on_b.revision_number(),
280            msg.proof_height_on_b.revision_height(),
281        );
282        let consensus_state_of_b_on_a =
283            client_val_ctx_a.consensus_state(&client_cons_state_path_on_a)?;
284        let ack_commitment = compute_ack_commitment(&msg.acknowledgement);
285        let ack_path_on_b =
286            AckPath::new(&packet.port_id_on_b, &packet.chan_id_on_b, packet.seq_on_a);
287
288        verify_conn_delay_passed(ctx_a, msg.proof_height_on_b, &conn_end_on_a)?;
289
290        // Verify the proof for the packet against the chain store.
291        client_state_of_b_on_a.verify_membership(
292            conn_end_on_a.counterparty().prefix(),
293            &msg.proof_acked_on_b,
294            consensus_state_of_b_on_a.root(),
295            Path::Ack(ack_path_on_b),
296            ack_commitment.into_vec(),
297        )?;
298    }
299
300    Ok(())
301}