penumbra_sdk_ibc/component/msg_handler/
acknowledgement.rs

1use anyhow::{Context, Result};
2use async_trait::async_trait;
3use cnidarium::StateWrite;
4use ibc_types::core::{
5    channel::channel::Order as ChannelOrder, channel::channel::State as ChannelState,
6    channel::events, channel::msgs::MsgAcknowledgement, channel::PortId,
7    connection::State as ConnectionState,
8};
9
10use crate::component::{
11    app_handler::{AppHandlerCheck, AppHandlerExecute},
12    channel::{StateReadExt as _, StateWriteExt as _},
13    connection::StateReadExt as _,
14    proof_verification::{commit_packet, PacketProofVerifier},
15    HostInterface, MsgHandler,
16};
17
18#[async_trait]
19impl MsgHandler for MsgAcknowledgement {
20    async fn check_stateless<H: AppHandlerCheck>(&self) -> Result<()> {
21        // NOTE: no additional stateless validation is possible
22
23        Ok(())
24    }
25
26    async fn try_execute<
27        S: StateWrite,
28        AH: AppHandlerCheck + AppHandlerExecute,
29        HI: HostInterface,
30    >(
31        &self,
32        mut state: S,
33    ) -> Result<()> {
34        tracing::debug!(msg = ?self);
35        let channel = state
36            .get_channel(&self.packet.chan_on_a, &self.packet.port_on_a)
37            .await?
38            .ok_or_else(|| anyhow::anyhow!("channel not found"))?;
39        if !channel.state_matches(&ChannelState::Open) {
40            anyhow::bail!("channel is not open");
41        }
42
43        // TODO: capability authentication?
44
45        if channel.counterparty().port_id().ne(&self.packet.port_on_b) {
46            anyhow::bail!("packet destination port does not match channel");
47        }
48        if channel
49            .counterparty()
50            .channel_id()
51            .ok_or_else(|| {
52                anyhow::anyhow!("missing channel id for counterparty channel in acknowledgement")
53            })?
54            .ne(&self.packet.chan_on_b)
55        {
56            anyhow::bail!("packet destination channel does not match channel");
57        }
58
59        let connection = state
60            .get_connection(&channel.connection_hops[0])
61            .await?
62            .ok_or_else(|| anyhow::anyhow!("connection not found for channel"))?;
63        if !connection.state_matches(&ConnectionState::Open) {
64            anyhow::bail!("connection for channel is not open");
65        }
66
67        // verify we sent this packet and haven't cleared it yet
68        let commitment = state
69            .get_packet_commitment(&self.packet)
70            .await?
71            .ok_or_else(|| anyhow::anyhow!("packet commitment not found"))?;
72        if commitment != commit_packet(&self.packet) {
73            anyhow::bail!("packet commitment does not match");
74        }
75
76        state
77            .verify_packet_ack_proof::<HI>(&connection, self)
78            .await
79            .with_context(|| "packet ack proof verification failed")?;
80
81        if channel.ordering == ChannelOrder::Ordered {
82            let next_sequence_ack = state
83                .get_ack_sequence(&self.packet.chan_on_a, &self.packet.port_on_a)
84                .await?;
85            if self.packet.sequence != next_sequence_ack.into() {
86                anyhow::bail!("packet sequence number does not match");
87            }
88        }
89
90        let transfer = PortId::transfer();
91        if self.packet.port_on_b == transfer {
92            AH::acknowledge_packet_check(&mut state, self).await?;
93        } else {
94            anyhow::bail!("invalid port id");
95        }
96        if channel.ordering == ChannelOrder::Ordered {
97            let mut next_sequence_ack = state
98                .get_ack_sequence(&self.packet.chan_on_a, &self.packet.port_on_a)
99                .await?;
100            next_sequence_ack += 1;
101            state.put_ack_sequence(
102                &self.packet.chan_on_a,
103                &self.packet.port_on_a,
104                next_sequence_ack,
105            );
106        }
107
108        // delete our commitment so we can't ack it again
109        state.delete_packet_commitment(
110            &self.packet.chan_on_a,
111            &self.packet.port_on_a,
112            self.packet.sequence.into(),
113        );
114
115        state.record(
116            events::packet::AcknowledgePacket {
117                timeout_height: self.packet.timeout_height_on_b,
118                timeout_timestamp: self.packet.timeout_timestamp_on_b,
119                sequence: self.packet.sequence,
120                src_port_id: self.packet.port_on_a.clone(),
121                src_channel_id: self.packet.chan_on_a.clone(),
122                dst_port_id: self.packet.port_on_b.clone(),
123                dst_channel_id: self.packet.chan_on_b.clone(),
124                channel_ordering: channel.ordering,
125                src_connection_id: channel.connection_hops[0].clone(),
126            }
127            .into(),
128        );
129
130        let transfer = PortId::transfer();
131        if self.packet.port_on_b == transfer {
132            AH::acknowledge_packet_execute(state, self).await?;
133        } else {
134            anyhow::bail!("invalid port id");
135        }
136
137        Ok(())
138    }
139}