penumbra_sdk_ibc/component/
channel.rs

1use crate::component::proof_verification::{commit_acknowledgement, commit_packet};
2use crate::prefix::MerklePrefixExt;
3use crate::IBC_COMMITMENT_PREFIX;
4
5use anyhow::Result;
6use async_trait::async_trait;
7
8use ibc_types::path::{
9    AckPath, ChannelEndPath, CommitmentPath, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath,
10};
11
12use cnidarium::{StateRead, StateWrite};
13use ibc_types::core::channel::{ChannelEnd, ChannelId, Packet, PortId};
14use penumbra_sdk_proto::{StateReadProto, StateWriteProto};
15
16// Note: many of the methods on this trait need to write raw bytes,
17// because the data they write is interpreted by counterparty chains.
18#[async_trait]
19pub trait StateWriteExt: StateWrite + StateReadExt {
20    fn put_channel_counter(&mut self, counter: u64) {
21        self.put_proto::<u64>("ibc_channel_counter".into(), counter);
22    }
23
24    async fn next_channel_id(&mut self) -> Result<ChannelId> {
25        let ctr = self.get_channel_counter().await?;
26        self.put_channel_counter(ctr + 1);
27
28        Ok(ChannelId::new(ctr))
29    }
30
31    fn put_channel(&mut self, channel_id: &ChannelId, port_id: &PortId, channel: ChannelEnd) {
32        self.put(
33            IBC_COMMITMENT_PREFIX
34                .apply_string(ChannelEndPath::new(port_id, channel_id).to_string()),
35            channel,
36        );
37    }
38
39    fn put_ack_sequence(&mut self, channel_id: &ChannelId, port_id: &PortId, sequence: u64) {
40        self.put_raw(
41            IBC_COMMITMENT_PREFIX.apply_string(SeqAckPath::new(port_id, channel_id).to_string()),
42            sequence.to_be_bytes().to_vec(),
43        );
44    }
45
46    fn put_recv_sequence(&mut self, channel_id: &ChannelId, port_id: &PortId, sequence: u64) {
47        self.put_raw(
48            IBC_COMMITMENT_PREFIX.apply_string(SeqRecvPath::new(port_id, channel_id).to_string()),
49            sequence.to_be_bytes().to_vec(),
50        );
51    }
52
53    fn put_send_sequence(&mut self, channel_id: &ChannelId, port_id: &PortId, sequence: u64) {
54        self.put_raw(
55            IBC_COMMITMENT_PREFIX.apply_string(SeqSendPath::new(port_id, channel_id).to_string()),
56            sequence.to_be_bytes().to_vec(),
57        );
58    }
59
60    fn put_packet_receipt(&mut self, packet: &Packet) {
61        self.put_raw(
62            IBC_COMMITMENT_PREFIX.apply_string(
63                ReceiptPath::new(&packet.port_on_b, &packet.chan_on_b, packet.sequence).to_string(),
64            ),
65            "1".into(),
66        );
67    }
68
69    fn put_packet_commitment(&mut self, packet: &Packet) {
70        let commitment_key = IBC_COMMITMENT_PREFIX.apply_string(
71            CommitmentPath::new(&packet.port_on_a, &packet.chan_on_a, packet.sequence).to_string(),
72        );
73        let packet_hash = commit_packet(packet);
74
75        self.put_raw(commitment_key, packet_hash);
76    }
77
78    fn delete_packet_commitment(
79        &mut self,
80        channel_id: &ChannelId,
81        port_id: &PortId,
82        sequence: u64,
83    ) {
84        self.delete(
85            IBC_COMMITMENT_PREFIX.apply_string(
86                CommitmentPath::new(port_id, channel_id, sequence.into()).to_string(),
87            ),
88        );
89    }
90
91    fn put_packet_acknowledgement(
92        &mut self,
93        port_id: &PortId,
94        channel_id: &ChannelId,
95        sequence: u64,
96        acknowledgement: &[u8],
97    ) {
98        self.put_raw(
99            IBC_COMMITMENT_PREFIX
100                .apply_string(AckPath::new(port_id, channel_id, sequence.into()).to_string()),
101            commit_acknowledgement(acknowledgement),
102        );
103    }
104}
105
106impl<T: StateWrite + ?Sized> StateWriteExt for T {}
107
108#[async_trait]
109pub trait StateReadExt: StateRead {
110    async fn get_channel_counter(&self) -> Result<u64> {
111        self.get_proto::<u64>("ibc_channel_counter")
112            .await
113            .map(|counter| counter.unwrap_or(0))
114    }
115
116    async fn get_channel(
117        &self,
118        channel_id: &ChannelId,
119        port_id: &PortId,
120    ) -> Result<Option<ChannelEnd>> {
121        self.get(
122            &IBC_COMMITMENT_PREFIX
123                .apply_string(ChannelEndPath::new(port_id, channel_id).to_string()),
124        )
125        .await
126    }
127
128    async fn get_recv_sequence(&self, channel_id: &ChannelId, port_id: &PortId) -> Result<u64> {
129        if let Some(be_bytes) = self
130            .get_raw(
131                &IBC_COMMITMENT_PREFIX
132                    .apply_string(SeqRecvPath::new(port_id, channel_id).to_string()),
133            )
134            .await?
135        {
136            // Parse big endian bytes into u64
137            Ok(u64::from_be_bytes(
138                be_bytes
139                    .try_into()
140                    .expect("written values are always 8-byte arrays"),
141            ))
142        } else {
143            // Default value for no key
144            Ok(0)
145        }
146    }
147
148    async fn get_ack_sequence(&self, channel_id: &ChannelId, port_id: &PortId) -> Result<u64> {
149        if let Some(be_bytes) = self
150            .get_raw(
151                &IBC_COMMITMENT_PREFIX
152                    .apply_string(SeqAckPath::new(port_id, channel_id).to_string()),
153            )
154            .await?
155        {
156            // Parse big endian bytes into u64
157            Ok(u64::from_be_bytes(
158                be_bytes
159                    .try_into()
160                    .expect("written values are always 8-byte arrays"),
161            ))
162        } else {
163            // Default value for no key
164            Ok(0)
165        }
166    }
167
168    async fn get_send_sequence(&self, channel_id: &ChannelId, port_id: &PortId) -> Result<u64> {
169        if let Some(be_bytes) = self
170            .get_raw(
171                &IBC_COMMITMENT_PREFIX
172                    .apply_string(SeqSendPath::new(port_id, channel_id).to_string()),
173            )
174            .await?
175        {
176            // Parse big endian bytes into u64
177            Ok(u64::from_be_bytes(
178                be_bytes
179                    .try_into()
180                    .expect("written values are always 8-byte arrays"),
181            ))
182        } else {
183            // Default value for no key
184            Ok(0)
185        }
186    }
187
188    async fn seen_packet(&self, packet: &Packet) -> Result<bool> {
189        self.get_raw(&IBC_COMMITMENT_PREFIX.apply_string(
190            ReceiptPath::new(&packet.port_on_b, &packet.chan_on_b, packet.sequence).to_string(),
191        ))
192        .await
193        .map(|res| res.is_some())
194    }
195
196    async fn seen_packet_by_channel(
197        &self,
198        channel_id: &ChannelId,
199        port_id: &PortId,
200        sequence: u64,
201    ) -> Result<bool> {
202        // TODO: make this logic more explicit
203        self.get_raw(
204            &IBC_COMMITMENT_PREFIX
205                .apply_string(ReceiptPath::new(port_id, channel_id, sequence.into()).to_string()),
206        )
207        .await
208        .map(|res| res.filter(|s| !s.is_empty()))
209        .map(|res| res.is_some())
210    }
211
212    async fn get_packet_commitment(&self, packet: &Packet) -> Result<Option<Vec<u8>>> {
213        let commitment = self
214            .get_raw(
215                &IBC_COMMITMENT_PREFIX.apply_string(
216                    CommitmentPath::new(&packet.port_on_a, &packet.chan_on_a, packet.sequence)
217                        .to_string(),
218                ),
219            )
220            .await?;
221
222        // this is for the special case where the commitment is empty, we consider this None.
223        if let Some(commitment) = commitment.as_ref() {
224            if commitment.is_empty() {
225                return Ok(None);
226            }
227        }
228
229        Ok(commitment)
230    }
231
232    async fn get_packet_commitment_by_id(
233        &self,
234        channel_id: &ChannelId,
235        port_id: &PortId,
236        sequence: u64,
237    ) -> Result<Option<Vec<u8>>> {
238        let commitment = self
239            .get_raw(&IBC_COMMITMENT_PREFIX.apply_string(
240                CommitmentPath::new(port_id, channel_id, sequence.into()).to_string(),
241            ))
242            .await?;
243
244        // this is for the special case where the commitment is empty, we consider this None.
245        if let Some(commitment) = commitment.as_ref() {
246            if commitment.is_empty() {
247                return Ok(None);
248            }
249        }
250
251        Ok(commitment)
252    }
253
254    async fn get_packet_acknowledgement(
255        &self,
256        port_id: &PortId,
257        channel_id: &ChannelId,
258        sequence: u64,
259    ) -> Result<Option<Vec<u8>>> {
260        self.get_raw(
261            &IBC_COMMITMENT_PREFIX
262                .apply_string(AckPath::new(port_id, channel_id, sequence.into()).to_string()),
263        )
264        .await
265    }
266}
267
268impl<T: StateRead + ?Sized> StateReadExt for T {}