1use crate::component::client::StateReadExt;
2
3use core::time::Duration;
4use ibc_proto::Protobuf;
5use ibc_types::path::{ClientConsensusStatePath, ClientUpgradePath};
6use ibc_types::DomainType;
7use ibc_types::{
8 core::{
9 channel::{
10 msgs::MsgAcknowledgement, msgs::MsgRecvPacket, msgs::MsgTimeout, ChannelEnd, ChannelId,
11 Packet, PortId,
12 },
13 client::ClientId,
14 client::Height,
15 commitment::{MerklePrefix, MerkleProof, MerkleRoot},
16 connection::ConnectionEnd,
17 },
18 lightclients::tendermint::{
19 client_state::ClientState as TendermintClientState,
20 consensus_state::ConsensusState as TendermintConsensusState,
21 },
22 path::{
23 AckPath, ChannelEndPath, ClientStatePath, CommitmentPath, ConnectionPath, Path,
24 ReceiptPath, SeqRecvPath,
25 },
26};
27
28use async_trait::async_trait;
29use cnidarium::StateRead;
30use num_traits::float::FloatCore;
31use sha2::{Digest, Sha256};
32
33use super::HostInterface;
34
35pub fn commit_packet(packet: &Packet) -> Vec<u8> {
41 let mut commit = vec![];
42 commit.extend_from_slice(&packet.timeout_timestamp_on_b.nanoseconds().to_be_bytes());
43 commit.extend_from_slice(
44 &packet
45 .timeout_height_on_b
46 .commitment_revision_number()
47 .to_be_bytes(),
48 );
49 commit.extend_from_slice(
50 &packet
51 .timeout_height_on_b
52 .commitment_revision_height()
53 .to_be_bytes(),
54 );
55 commit.extend_from_slice(&Sha256::digest(&packet.data)[..]);
56
57 Sha256::digest(&commit).to_vec()
58}
59
60pub fn commit_acknowledgement(ack_data: &[u8]) -> Vec<u8> {
64 Sha256::digest(ack_data).to_vec()
65}
66
67pub fn calculate_block_delay(
68 delay_period_time: &Duration,
69 max_expected_time_per_block: &Duration,
70) -> u64 {
71 if max_expected_time_per_block.is_zero() {
72 return 0;
73 }
74
75 FloatCore::ceil(delay_period_time.as_secs_f64() / max_expected_time_per_block.as_secs_f64())
76 as u64
77}
78
79fn verify_merkle_absence_proof(
80 proof_specs: &[ics23::ProofSpec],
81 prefix: &MerklePrefix,
82 proof: &MerkleProof,
83 root: &MerkleRoot,
84 path: impl Into<Path>,
85) -> anyhow::Result<()> {
86 let merkle_path = prefix.apply(vec![path.into().to_string()]);
87 proof.verify_non_membership(proof_specs, root.clone(), merkle_path)?;
88
89 Ok(())
90}
91
92fn verify_merkle_proof(
93 proof_specs: &[ics23::ProofSpec],
94 prefix: &MerklePrefix,
95 proof: &MerkleProof,
96 root: &MerkleRoot,
97 path: impl Into<Path>,
98 value: Vec<u8>,
99) -> anyhow::Result<()> {
100 let merkle_path = prefix.apply(vec![path.into().to_string()]);
101 tracing::debug!(
102 ?root,
103 ?merkle_path,
104 value = ?hex::encode(&value),
105 );
106 proof.verify_membership(proof_specs, root.clone(), merkle_path, value, 0)?;
107
108 Ok(())
109}
110
111#[async_trait]
112pub trait ClientUpgradeProofVerifier: StateReadExt + Sized {
113 async fn verify_client_upgrade_proof<HI: HostInterface>(
114 &self,
115 client_id: &ClientId,
116 client_state_proof: &MerkleProof,
117 consensus_state_proof: &MerkleProof,
118 upgraded_tm_consensus_state: TendermintConsensusState,
119 upgraded_tm_client_state: TendermintClientState,
120 ) -> anyhow::Result<()> {
121 let trusted_client_state = self.get_client_state(client_id).await?;
123
124 let mut upgrade_path = trusted_client_state.upgrade_path.clone();
126 if upgrade_path.pop().is_none() {
127 anyhow::bail!("upgrade path is not set");
128 };
129
130 let upgrade_path_prefix =
131 MerklePrefix::try_from(upgrade_path.clone().concat().into_bytes()).map_err(|_| {
132 anyhow::anyhow!("couldn't create commitment prefix from client upgrade path")
133 })?;
134
135 if trusted_client_state.is_frozen() {
137 anyhow::bail!("client is frozen");
138 }
139
140 let trusted_consensus_state = self
142 .get_verified_consensus_state(&trusted_client_state.latest_height(), client_id)
143 .await?;
144
145 let now = HI::get_block_timestamp(&self).await?;
147 let time_elapsed = now.duration_since(trusted_consensus_state.timestamp)?;
148
149 if trusted_client_state.expired(time_elapsed) {
150 anyhow::bail!("client is expired");
151 }
152
153 verify_merkle_proof(
154 &trusted_client_state.proof_specs,
155 &upgrade_path_prefix,
156 client_state_proof,
157 &trusted_consensus_state.root,
158 ClientUpgradePath::UpgradedClientState(
159 trusted_client_state.latest_height().revision_height(),
160 ),
161 upgraded_tm_client_state.encode_to_vec(),
162 )?;
163
164 verify_merkle_proof(
165 &trusted_client_state.proof_specs,
166 &upgrade_path_prefix,
167 consensus_state_proof,
168 &trusted_consensus_state.root,
169 ClientUpgradePath::UpgradedClientConsensusState(
170 trusted_client_state.latest_height().revision_height(),
171 ),
172 upgraded_tm_consensus_state.encode_to_vec(),
173 )?;
174
175 Ok(())
176 }
177}
178
179impl<T: StateRead> ClientUpgradeProofVerifier for T {}
180
181#[async_trait]
182pub trait ChannelProofVerifier: StateReadExt {
183 async fn verify_channel_proof(
184 &self,
185 connection: &ConnectionEnd,
186 proof: &MerkleProof,
187 proof_height: &Height,
188 channel_id: &ChannelId,
189 port_id: &PortId,
190 expected_channel: &ChannelEnd,
191 ) -> anyhow::Result<()> {
192 let trusted_client_state = self.get_client_state(&connection.client_id).await?;
194
195 if trusted_client_state.is_frozen() {
198 anyhow::bail!("client is frozen");
199 }
200
201 let trusted_consensus_state = self
203 .get_verified_consensus_state(proof_height, &connection.client_id)
204 .await?;
205
206 trusted_client_state.verify_height(*proof_height)?;
207
208 let value = expected_channel.clone().encode_vec();
210
211 verify_merkle_proof(
212 &trusted_client_state.proof_specs,
213 &connection.counterparty.prefix.clone(),
214 proof,
215 &trusted_consensus_state.root,
216 ChannelEndPath::new(port_id, channel_id),
217 value,
218 )?;
219
220 Ok(())
221 }
222}
223
224impl<T: StateRead> ChannelProofVerifier for T {}
225
226pub fn verify_connection_state(
227 client_state: &TendermintClientState,
228 height: Height,
229 prefix: &MerklePrefix,
230 proof: &MerkleProof,
231 root: &MerkleRoot,
232 conn_path: &ConnectionPath,
233 expected_connection_end: &ConnectionEnd,
234) -> anyhow::Result<()> {
235 client_state.verify_height(height)?;
236
237 let value = expected_connection_end.clone().encode_vec();
239
240 verify_merkle_proof(
241 &client_state.proof_specs,
242 prefix,
243 proof,
244 root,
245 conn_path.clone(),
246 value,
247 )?;
248
249 Ok(())
250}
251
252pub fn verify_client_full_state(
253 client_state: &TendermintClientState,
254 height: Height,
255 prefix: &MerklePrefix,
256 proof: &MerkleProof,
257 root: &MerkleRoot,
258 client_state_path: &ClientStatePath,
259 expected_client_state: TendermintClientState,
260) -> anyhow::Result<()> {
261 client_state.verify_height(height)?;
262
263 let value: Vec<u8> = expected_client_state.encode_to_vec();
264
265 verify_merkle_proof(
266 &client_state.proof_specs,
267 prefix,
268 proof,
269 root,
270 client_state_path.clone(),
271 value,
272 )?;
273
274 Ok(())
275}
276
277pub fn verify_client_consensus_state(
278 client_state: &TendermintClientState,
279 height: Height,
280 prefix: &MerklePrefix,
281 proof: &MerkleProof,
282 root: &MerkleRoot,
283 client_cons_state_path: &ClientConsensusStatePath,
284 expected_consenus_state: TendermintConsensusState,
285) -> anyhow::Result<()> {
286 client_state.verify_height(height)?;
287
288 let value: Vec<u8> = expected_consenus_state.encode_to_vec();
289
290 verify_merkle_proof(
291 &client_state.proof_specs,
292 prefix,
293 proof,
294 root,
295 client_cons_state_path.clone(),
296 value,
297 )?;
298
299 Ok(())
300}
301
302#[async_trait]
303pub trait PacketProofVerifier: StateReadExt + inner::Inner {
304 async fn verify_packet_recv_proof<HI: HostInterface>(
305 &self,
306 connection: &ConnectionEnd,
307 msg: &MsgRecvPacket,
308 ) -> anyhow::Result<()> {
309 let (trusted_client_state, trusted_consensus_state) = self
310 .get_trusted_client_and_consensus_state::<HI>(
311 &connection.client_id,
312 &msg.proof_height_on_a,
313 connection,
314 )
315 .await?;
316
317 let commitment_path = CommitmentPath {
318 port_id: msg.packet.port_on_a.clone(),
319 channel_id: msg.packet.chan_on_a.clone(),
320 sequence: msg.packet.sequence,
321 };
322
323 let commitment_bytes = commit_packet(&msg.packet);
324
325 verify_merkle_proof(
326 &trusted_client_state.proof_specs,
327 &connection.counterparty.prefix.clone(),
328 &msg.proof_commitment_on_a,
329 &trusted_consensus_state.root,
330 commitment_path,
331 commitment_bytes,
332 )?;
333
334 Ok(())
335 }
336
337 async fn verify_packet_ack_proof<HI: HostInterface>(
338 &self,
339 connection: &ConnectionEnd,
340 msg: &MsgAcknowledgement,
341 ) -> anyhow::Result<()> {
342 let (trusted_client_state, trusted_consensus_state) = self
343 .get_trusted_client_and_consensus_state::<HI>(
344 &connection.client_id,
345 &msg.proof_height_on_b,
346 connection,
347 )
348 .await?;
349
350 let ack_path = AckPath {
351 port_id: msg.packet.port_on_b.clone(),
352 channel_id: msg.packet.chan_on_b.clone(),
353 sequence: msg.packet.sequence,
354 };
355
356 let ack_bytes = commit_acknowledgement(&msg.acknowledgement);
357
358 verify_merkle_proof(
359 &trusted_client_state.proof_specs,
360 &connection.counterparty.prefix.clone(),
361 &msg.proof_acked_on_b,
362 &trusted_consensus_state.root,
363 ack_path,
364 ack_bytes,
365 )?;
366
367 Ok(())
368 }
369
370 async fn verify_packet_timeout_proof<HI: HostInterface>(
371 &self,
372 connection: &ConnectionEnd,
373 msg: &MsgTimeout,
374 ) -> anyhow::Result<()> {
375 let (trusted_client_state, trusted_consensus_state) = self
376 .get_trusted_client_and_consensus_state::<HI>(
377 &connection.client_id,
378 &msg.proof_height_on_b,
379 connection,
380 )
381 .await?;
382
383 let seq_bytes = msg.next_seq_recv_on_b.0.to_be_bytes().to_vec();
384 let seq_path = SeqRecvPath(msg.packet.port_on_b.clone(), msg.packet.chan_on_b.clone());
385
386 verify_merkle_proof(
387 &trusted_client_state.proof_specs,
388 &connection.counterparty.prefix.clone(),
389 &msg.proof_unreceived_on_b,
390 &trusted_consensus_state.root,
391 seq_path,
392 seq_bytes,
393 )?;
394
395 Ok(())
396 }
397
398 async fn verify_packet_timeout_absence_proof<HI: HostInterface>(
399 &self,
400 connection: &ConnectionEnd,
401 msg: &MsgTimeout,
402 ) -> anyhow::Result<()> {
403 let (trusted_client_state, trusted_consensus_state) = self
404 .get_trusted_client_and_consensus_state::<HI>(
405 &connection.client_id,
406 &msg.proof_height_on_b,
407 connection,
408 )
409 .await?;
410
411 let receipt_path = ReceiptPath {
412 port_id: msg.packet.port_on_b.clone(),
413 channel_id: msg.packet.chan_on_b.clone(),
414 sequence: msg.packet.sequence,
415 };
416
417 verify_merkle_absence_proof(
418 &trusted_client_state.proof_specs,
419 &connection.counterparty.prefix.clone(),
420 &msg.proof_unreceived_on_b,
421 &trusted_consensus_state.root,
422 receipt_path,
423 )?;
424
425 Ok(())
426 }
427}
428
429impl<T: StateRead> PacketProofVerifier for T {}
430
431mod inner {
432 use crate::component::HostInterface;
433
434 use super::*;
435
436 #[async_trait]
437 pub trait Inner: StateReadExt + Sized {
438 async fn get_trusted_client_and_consensus_state<HI: HostInterface>(
439 &self,
440 client_id: &ClientId,
441 height: &Height,
442 connection: &ConnectionEnd,
443 ) -> anyhow::Result<(TendermintClientState, TendermintConsensusState)> {
444 let trusted_client_state = self.get_client_state(client_id).await?;
445
446 if trusted_client_state.is_frozen() {
448 anyhow::bail!("client is frozen");
449 }
450
451 let trusted_consensus_state =
452 self.get_verified_consensus_state(height, client_id).await?;
453
454 let tm_client_state = trusted_client_state;
455
456 tm_client_state.verify_height(*height)?;
457
458 let current_timestamp = HI::get_block_timestamp(&self).await?;
461 let current_height = HI::get_block_height(&self).await?;
462 let processed_height = self.get_client_update_height(client_id, height).await?;
463 let processed_time = self.get_client_update_time(client_id, height).await?;
464
465 let max_time_per_block = std::time::Duration::from_secs(20);
467
468 let delay_period_time = connection.delay_period;
469 let delay_period_blocks =
470 calculate_block_delay(&delay_period_time, &max_time_per_block);
471
472 TendermintClientState::verify_delay_passed(
473 current_timestamp.into(),
474 Height::new(HI::get_revision_number(self).await?, current_height)?,
475 processed_time,
476 processed_height,
477 delay_period_time,
478 delay_period_blocks,
479 )?;
480
481 Ok((tm_client_state, trusted_consensus_state))
482 }
483 }
484
485 impl<T: StateReadExt> Inner for T {}
486}