penumbra_sdk_ibc/component/msg_handler/
acknowledgement.rs1use 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 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 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 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 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}