penumbra_sdk_ibc/component/
channel.rs1use 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#[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 Ok(u64::from_be_bytes(
138 be_bytes
139 .try_into()
140 .expect("written values are always 8-byte arrays"),
141 ))
142 } else {
143 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 Ok(u64::from_be_bytes(
158 be_bytes
159 .try_into()
160 .expect("written values are always 8-byte arrays"),
161 ))
162 } else {
163 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 Ok(u64::from_be_bytes(
178 be_bytes
179 .try_into()
180 .expect("written values are always 8-byte arrays"),
181 ))
182 } else {
183 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 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 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 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 {}