1use crate::{
2 events::{IbcPacket, IbcPacketTimeoutHeight},
3 ibc_types::{
4 IbcChannelId, IbcChannelOrdering, IbcChannelVersion, IbcClientId, IbcConnectionId,
5 IbcPortId,
6 },
7 prelude::*,
8 querier::{
9 abci::AbciProofKind,
10 ibc::{IbcChannelProofs, IbcConnectionProofs},
11 },
12};
13use std::sync::LazyLock;
14
15impl SigningClient {
18 pub async fn ibc_create_client_msg(
19 &self,
20 trusting_period_secs: Option<u64>,
21 remote_querier: &QueryClient,
22 ) -> Result<layer_climb_proto::ibc::client::MsgCreateClient> {
23 let (client_state, consensus_state) = remote_querier
24 .ibc_create_client_consensus_state(trusting_period_secs)
25 .await?;
26
27 Ok(layer_climb_proto::ibc::client::MsgCreateClient {
28 signer: self.addr.to_string(),
29 consensus_state: Some(proto_into_any(&consensus_state)?),
30 client_state: Some(proto_into_any(&client_state)?),
31 })
32 }
33
34 pub async fn ibc_update_client_msg(
35 &self,
36 client_id: &IbcClientId,
37 remote_querier: &QueryClient,
38 trusted_height: Option<layer_climb_proto::RevisionHeight>,
39 ) -> Result<layer_climb_proto::ibc::client::MsgUpdateClient> {
40 let trusted_height = match trusted_height {
46 None => *self
47 .querier
48 .ibc_client_state(client_id, None)
49 .await?
50 .latest_height
51 .as_ref()
52 .context("missing latest height")?,
53 Some(trusted_height) => trusted_height,
54 };
55
56 remote_querier
57 .wait_until_block_height(trusted_height.revision_height + 1, None)
58 .await?;
59
60 let curr_signed_header = remote_querier.fetch_signed_header(None).await?;
62 let curr_header = curr_signed_header
63 .header
64 .as_ref()
65 .context("missing curr header")?;
66 let trusted_signed_header = remote_querier
68 .fetch_signed_header(Some(trusted_height.revision_height + 1))
69 .await?;
70 let trusted_header = trusted_signed_header
71 .header
72 .as_ref()
73 .context("missing trusted header")?;
74
75 let validator_set = remote_querier
76 .validator_set(
77 Some(curr_header.height.try_into()?),
78 Some(&curr_header.proposer_address),
79 )
80 .await?;
81 let trusted_validators = remote_querier
82 .validator_set(
83 Some(trusted_header.height.try_into()?),
84 Some(&trusted_header.proposer_address),
85 )
86 .await?;
87
88 let header = layer_climb_proto::ibc::light_client::Header {
89 signed_header: Some(curr_signed_header),
90 trusted_height: Some(trusted_height),
91 validator_set: Some(validator_set),
92 trusted_validators: Some(trusted_validators),
93 };
94
95 Ok(layer_climb_proto::ibc::client::MsgUpdateClient {
96 client_id: client_id.to_string(),
97 signer: self.addr.to_string(),
98 client_message: Some(proto_into_any(&header)?),
102 })
103 }
104
105 pub async fn ibc_open_connection_init_msg(
106 &self,
107 client_id: &IbcClientId,
108 counterparty_client_id: &IbcClientId,
109 ) -> Result<layer_climb_proto::ibc::connection::MsgConnectionOpenInit> {
110 Ok(layer_climb_proto::ibc::connection::MsgConnectionOpenInit {
111 client_id: client_id.to_string(),
112 counterparty: Some(layer_climb_proto::ibc::connection::Counterparty {
113 client_id: counterparty_client_id.to_string(),
114 connection_id: "".to_string(),
116 prefix: Some(IBC_MERKLE_PREFIX.clone()),
117 }),
118 version: Some(IBC_VERSION.clone()),
119 delay_period: 0,
121 signer: self.addr.to_string(),
122 })
123 }
124
125 pub async fn ibc_open_connection_try_msg(
126 &self,
127 client_id: &IbcClientId,
128 counterparty_client_id: &IbcClientId,
129 counterparty_connection_id: &IbcConnectionId,
130 remote_querier: &QueryClient,
131 ) -> Result<layer_climb_proto::ibc::connection::MsgConnectionOpenTry> {
132 let IbcConnectionProofs {
133 proof_height,
134 consensus_height,
135 connection,
136 connection_proof,
137 client_state_proof,
138 consensus_proof,
139 client_state,
140 ..
141 } = remote_querier
142 .ibc_connection_proofs(
143 self.querier
144 .ibc_client_state(client_id, None)
145 .await?
146 .latest_height
147 .context("missing latest height")?,
148 counterparty_client_id,
149 counterparty_connection_id,
150 )
151 .await?;
152
153 if connection.state() != layer_climb_proto::ibc::connection::State::Init {
154 bail!(
155 "counterparty connection state is not Init, instead it is {:?}",
156 connection.state()
157 );
158 }
159
160 #[allow(deprecated)]
161 Ok(layer_climb_proto::ibc::connection::MsgConnectionOpenTry {
162 client_id: client_id.to_string(),
163 client_state: Some(proto_into_any(&client_state)?),
164 proof_height: Some(proof_height),
165 proof_client: client_state_proof,
166 proof_init: connection_proof,
167 proof_consensus: consensus_proof,
168 consensus_height: Some(consensus_height),
169 counterparty_versions: vec![IBC_VERSION.clone()],
170 counterparty: Some(layer_climb_proto::ibc::connection::Counterparty {
171 client_id: counterparty_client_id.to_string(),
172 connection_id: counterparty_connection_id.to_string(),
173 prefix: Some(IBC_MERKLE_PREFIX.clone()),
174 }),
175 signer: self.addr.to_string(),
176 host_consensus_state_proof: Vec::new(),
178 previous_connection_id: "".to_string(),
180 delay_period: 0,
182 })
183 }
184
185 pub async fn ibc_open_connection_ack_msg(
186 &self,
187 client_id: &IbcClientId,
188 counterparty_client_id: &IbcClientId,
189 connection_id: &IbcConnectionId,
190 counterparty_connection_id: &IbcConnectionId,
191 remote_querier: &QueryClient,
192 ) -> Result<layer_climb_proto::ibc::connection::MsgConnectionOpenAck> {
193 let IbcConnectionProofs {
194 query_height,
195 proof_height,
196 consensus_height,
197 connection,
198 connection_proof,
199 client_state_proof,
200 consensus_proof,
201 client_state,
202 } = remote_querier
203 .ibc_connection_proofs(
204 self.querier
205 .ibc_client_state(client_id, None)
206 .await?
207 .latest_height
208 .context("missing latest height")?,
209 counterparty_client_id,
210 counterparty_connection_id,
211 )
212 .await?;
213
214 if connection.state() != layer_climb_proto::ibc::connection::State::Tryopen {
215 bail!(
216 "counterparty connection state is not TryOpen at height {}, instead it is {:?}",
217 query_height,
218 connection.state(),
219 );
220 }
221
222 #[allow(deprecated)]
223 Ok(layer_climb_proto::ibc::connection::MsgConnectionOpenAck {
224 connection_id: connection_id.to_string(),
225 counterparty_connection_id: counterparty_connection_id.to_string(),
226 client_state: Some(proto_into_any(&client_state)?),
227 proof_height: Some(proof_height),
228 proof_client: client_state_proof,
229 proof_try: connection_proof,
230 proof_consensus: consensus_proof,
231 consensus_height: Some(consensus_height),
232 signer: self.addr.to_string(),
233 version: Some(IBC_VERSION.clone()),
234 host_consensus_state_proof: Vec::new(),
236 })
237 }
238
239 pub async fn ibc_open_connection_confirm_msg(
240 &self,
241 client_id: &IbcClientId,
242 counterparty_client_id: &IbcClientId,
243 connection_id: &IbcConnectionId,
244 counterparty_connection_id: &IbcConnectionId,
245 remote_querier: &QueryClient,
246 ) -> Result<layer_climb_proto::ibc::connection::MsgConnectionOpenConfirm> {
247 let IbcConnectionProofs {
248 proof_height,
249 connection_proof,
250 ..
251 } = remote_querier
252 .ibc_connection_proofs(
253 self.querier
254 .ibc_client_state(client_id, None)
255 .await?
256 .latest_height
257 .context("missing latest height")?,
258 counterparty_client_id,
259 counterparty_connection_id,
260 )
261 .await?;
262
263 #[allow(deprecated)]
264 Ok(
265 layer_climb_proto::ibc::connection::MsgConnectionOpenConfirm {
266 connection_id: connection_id.to_string(),
267 proof_ack: connection_proof,
268 proof_height: Some(proof_height),
269 signer: self.addr.to_string(),
270 },
271 )
272 }
273
274 pub fn ibc_open_channel_init_msg(
275 &self,
276 connection_id: &IbcConnectionId,
277 port_id: &IbcPortId,
278 version: &IbcChannelVersion,
279 ordering: IbcChannelOrdering,
280 counterparty_port_id: &IbcPortId,
281 ) -> Result<layer_climb_proto::ibc::channel::MsgChannelOpenInit> {
282 #[allow(deprecated)]
283 Ok(layer_climb_proto::ibc::channel::MsgChannelOpenInit {
284 port_id: port_id.to_string(),
285 channel: Some(layer_climb_proto::ibc::channel::Channel {
286 state: layer_climb_proto::ibc::channel::State::Init as i32,
287 ordering: match ordering {
288 IbcChannelOrdering::Ordered => {
289 layer_climb_proto::ibc::channel::Order::Ordered as i32
290 }
291 IbcChannelOrdering::Unordered => {
292 layer_climb_proto::ibc::channel::Order::Unordered as i32
293 }
294 },
295 counterparty: Some(layer_climb_proto::ibc::channel::Counterparty {
296 port_id: counterparty_port_id.to_string(),
297 channel_id: "".to_string(),
298 }),
299 connection_hops: vec![connection_id.to_string()],
300 version: version.to_string(),
301 upgrade_sequence: 0,
302 }),
303 signer: self.addr.to_string(),
304 })
305 }
306
307 #[allow(clippy::too_many_arguments)]
308 pub async fn ibc_open_channel_try_msg(
309 &self,
310 client_id: &IbcClientId,
311 connection_id: &IbcConnectionId,
312 port_id: &IbcPortId,
313 version: &IbcChannelVersion,
314 counterparty_port_id: &IbcPortId,
315 counterparty_channel_id: &IbcChannelId,
316 counterparty_version: &IbcChannelVersion,
317 ordering: IbcChannelOrdering,
318 remote_querier: &QueryClient,
319 ) -> Result<layer_climb_proto::ibc::channel::MsgChannelOpenTry> {
320 let IbcChannelProofs {
321 proof_height,
322 channel_proof,
323 ..
324 } = remote_querier
325 .ibc_channel_proofs(
326 self.querier
327 .ibc_client_state(client_id, None)
328 .await?
329 .latest_height
330 .context("missing latest height")?,
331 counterparty_channel_id,
332 counterparty_port_id,
333 )
334 .await?;
335
336 #[allow(deprecated)]
337 Ok(layer_climb_proto::ibc::channel::MsgChannelOpenTry {
338 port_id: port_id.to_string(),
339 previous_channel_id: "".to_string(),
340 channel: Some(layer_climb_proto::ibc::channel::Channel {
341 state: layer_climb_proto::ibc::channel::State::Tryopen as i32,
342 ordering: match ordering {
343 IbcChannelOrdering::Ordered => {
344 layer_climb_proto::ibc::channel::Order::Ordered as i32
345 }
346 IbcChannelOrdering::Unordered => {
347 layer_climb_proto::ibc::channel::Order::Unordered as i32
348 }
349 },
350 counterparty: Some(layer_climb_proto::ibc::channel::Counterparty {
351 port_id: counterparty_port_id.to_string(),
352 channel_id: counterparty_channel_id.to_string(),
353 }),
354 connection_hops: vec![connection_id.to_string()],
355 version: version.to_string(),
356 upgrade_sequence: 0,
357 }),
358 counterparty_version: counterparty_version.to_string(),
359 proof_init: channel_proof,
360 proof_height: Some(proof_height),
361 signer: self.addr.to_string(),
362 })
363 }
364
365 #[allow(clippy::too_many_arguments)]
366 pub async fn ibc_open_channel_ack_msg(
367 &self,
368 client_id: &IbcClientId,
369 channel_id: &IbcChannelId,
370 port_id: &IbcPortId,
371 counterparty_port_id: &IbcPortId,
372 counterparty_channel_id: &IbcChannelId,
373 counterparty_version: &IbcChannelVersion,
374 remote_querier: &QueryClient,
375 ) -> Result<layer_climb_proto::ibc::channel::MsgChannelOpenAck> {
376 let IbcChannelProofs {
377 proof_height,
378 channel_proof,
379 ..
380 } = remote_querier
381 .ibc_channel_proofs(
382 self.querier
383 .ibc_client_state(client_id, None)
384 .await?
385 .latest_height
386 .context("missing latest height")?,
387 counterparty_channel_id,
388 counterparty_port_id,
389 )
390 .await?;
391
392 #[allow(deprecated)]
393 Ok(layer_climb_proto::ibc::channel::MsgChannelOpenAck {
394 port_id: port_id.to_string(),
395 channel_id: channel_id.to_string(),
396 counterparty_channel_id: counterparty_channel_id.to_string(),
397 counterparty_version: counterparty_version.to_string(),
398 proof_try: channel_proof,
399 proof_height: Some(proof_height),
400 signer: self.addr.to_string(),
401 })
402 }
403
404 pub async fn ibc_open_channel_confirm_msg(
405 &self,
406 client_id: &IbcClientId,
407 channel_id: &IbcChannelId,
408 port_id: &IbcPortId,
409 counterparty_port_id: &IbcPortId,
410 counterparty_channel_id: &IbcChannelId,
411 remote_querier: &QueryClient,
412 ) -> Result<layer_climb_proto::ibc::channel::MsgChannelOpenConfirm> {
413 let IbcChannelProofs {
414 proof_height,
415 channel_proof,
416 ..
417 } = remote_querier
418 .ibc_channel_proofs(
419 self.querier
420 .ibc_client_state(client_id, None)
421 .await?
422 .latest_height
423 .context("missing latest height")?,
424 counterparty_channel_id,
425 counterparty_port_id,
426 )
427 .await?;
428
429 #[allow(deprecated)]
430 Ok(layer_climb_proto::ibc::channel::MsgChannelOpenConfirm {
431 port_id: port_id.to_string(),
432 channel_id: channel_id.to_string(),
433 proof_ack: channel_proof,
434 proof_height: Some(proof_height),
435 signer: self.addr.to_string(),
436 })
437 }
438
439 pub async fn ibc_packet_recv_msg(
440 &self,
441 client_id: &IbcClientId,
442 packet: IbcPacket,
443 remote_querier: &QueryClient,
444 ) -> Result<layer_climb_proto::ibc::channel::MsgRecvPacket> {
445 let proof_height = self
446 .querier
447 .ibc_client_state(client_id, None)
448 .await?
449 .latest_height
450 .context("missing latest height")?;
451
452 let query_height = proof_height.revision_height - 1;
453
454 let packet_commitment_store = remote_querier
455 .abci_proof(
456 AbciProofKind::IbcPacketCommitment {
457 port_id: packet.src_port_id.clone(),
458 channel_id: packet.src_channel_id.clone(),
459 sequence: packet.sequence,
460 },
461 Some(query_height),
462 )
463 .await?;
464
465 if packet_commitment_store.value.is_empty() {
466 bail!("packet commitment value is empty");
467 }
468
469 if packet_commitment_store.proof.is_empty() {
470 bail!("packet commitment proof is empty");
471 }
472
473 Ok(layer_climb_proto::ibc::channel::MsgRecvPacket {
474 packet: Some(convert_ibc_packet(&packet)?),
475 proof_commitment: packet_commitment_store.proof,
476 proof_height: Some(proof_height),
477 signer: self.addr.to_string(),
478 })
479 }
480
481 pub async fn ibc_packet_ack_msg(
482 &self,
483 client_id: &IbcClientId,
484 mut packet: IbcPacket,
485 remote_querier: &QueryClient,
486 ) -> Result<layer_climb_proto::ibc::channel::MsgAcknowledgement> {
487 let proof_height = self
488 .querier
489 .ibc_client_state(client_id, None)
490 .await?
491 .latest_height
492 .context("missing latest height")?;
493
494 let query_height = proof_height.revision_height - 1;
495
496 let packet_ack_store = remote_querier
497 .abci_proof(
498 AbciProofKind::IbcPacketAck {
499 port_id: packet.src_port_id.clone(),
500 channel_id: packet.src_channel_id.clone(),
501 sequence: packet.sequence,
502 },
503 Some(query_height),
504 )
505 .await?;
506
507 if packet_ack_store.value.is_empty() {
508 bail!("packet ack value is empty");
509 }
510
511 if packet_ack_store.proof.is_empty() {
512 bail!("packet ack proof is empty");
513 }
514
515 let acknowledgement = packet.ack.take().context("packet ack is missing")?;
516
517 if acknowledgement.is_empty() {
518 bail!("acknowledgement is empty");
519 }
520
521 packet.invert();
524
525 Ok(layer_climb_proto::ibc::channel::MsgAcknowledgement {
526 packet: Some(convert_ibc_packet(&packet)?),
527 acknowledgement,
528 proof_acked: packet_ack_store.proof,
529 proof_height: Some(proof_height),
530 signer: self.addr.to_string(),
531 })
532 }
533}
534
535pub static IBC_VERSION: LazyLock<layer_climb_proto::ibc::connection::Version> =
536 LazyLock::new(|| {
537 layer_climb_proto::ibc::connection::Version {
538 identifier: "1".to_string(),
540 features: vec!["ORDER_ORDERED".to_string(), "ORDER_UNORDERED".to_string()],
542 }
543 });
544
545pub static IBC_MERKLE_PREFIX: LazyLock<layer_climb_proto::MerklePrefix> = LazyLock::new(|| {
546 layer_climb_proto::MerklePrefix {
547 key_prefix: "ibc".as_bytes().to_vec(),
553 }
554});
555
556fn convert_ibc_packet(packet: &IbcPacket) -> Result<layer_climb_proto::ibc::channel::Packet> {
557 Ok(layer_climb_proto::ibc::channel::Packet {
558 sequence: packet.sequence,
559 source_port: packet.src_port_id.to_string(),
560 source_channel: packet.src_channel_id.to_string(),
561 destination_port: packet.dst_port_id.to_string(),
562 destination_channel: packet.dst_channel_id.to_string(),
563 timeout_height: match packet.timeout_height {
564 IbcPacketTimeoutHeight::Revision { revision, height } => {
565 Some(layer_climb_proto::RevisionHeight {
566 revision_number: revision,
567 revision_height: height,
568 })
569 }
570 IbcPacketTimeoutHeight::None => None,
571 },
572 timeout_timestamp: packet.timeout_timestamp,
573 data: packet.data.clone().unwrap_or_default(),
574 })
575}