ibc_query/core/channel/
query.rs

1//! Provides utility functions for querying IBC channel states.
2
3use ibc::core::client::context::ClientValidationContext;
4use ibc::core::host::types::path::{
5    AckPath, ChannelEndPath, ClientConsensusStatePath, ClientStatePath, CommitmentPath, Path,
6    ReceiptPath, SeqRecvPath, SeqSendPath,
7};
8use ibc::core::host::{ConsensusStateRef, ValidationContext};
9use ibc::primitives::prelude::format;
10use ibc_proto::google::protobuf::Any;
11
12use super::{
13    QueryChannelClientStateRequest, QueryChannelClientStateResponse,
14    QueryChannelConsensusStateRequest, QueryChannelConsensusStateResponse, QueryChannelRequest,
15    QueryChannelResponse, QueryChannelsRequest, QueryChannelsResponse,
16    QueryConnectionChannelsRequest, QueryConnectionChannelsResponse,
17    QueryNextSequenceReceiveRequest, QueryNextSequenceReceiveResponse,
18    QueryNextSequenceSendRequest, QueryNextSequenceSendResponse, QueryPacketAcknowledgementRequest,
19    QueryPacketAcknowledgementResponse, QueryPacketAcknowledgementsRequest,
20    QueryPacketAcknowledgementsResponse, QueryPacketCommitmentRequest,
21    QueryPacketCommitmentResponse, QueryPacketCommitmentsRequest, QueryPacketCommitmentsResponse,
22    QueryPacketReceiptRequest, QueryPacketReceiptResponse, QueryUnreceivedAcksRequest,
23    QueryUnreceivedAcksResponse, QueryUnreceivedPacketsRequest, QueryUnreceivedPacketsResponse,
24};
25use crate::core::client::IdentifiedClientState;
26use crate::core::context::{ProvableContext, QueryContext};
27use crate::error::QueryError;
28
29/// Queries for a specific IBC channel by the given channel and port ids and
30/// returns the channel end with the associated proof.
31pub fn query_channel<I>(
32    ibc_ctx: &I,
33    request: &QueryChannelRequest,
34) -> Result<QueryChannelResponse, QueryError>
35where
36    I: ValidationContext + ProvableContext,
37{
38    let channel_end_path = ChannelEndPath::new(&request.port_id, &request.channel_id);
39
40    let channel_end = ibc_ctx.channel_end(&channel_end_path)?;
41
42    let proof_height = match request.query_height {
43        Some(height) => height,
44        None => ibc_ctx.host_height()?,
45    };
46
47    let proof = ibc_ctx
48        .get_proof(proof_height, &Path::ChannelEnd(channel_end_path.clone()))
49        .ok_or_else(|| {
50            QueryError::missing_proof(format!(
51                "Proof not found for channel end path {channel_end_path:?}"
52            ))
53        })?;
54
55    Ok(QueryChannelResponse::new(channel_end, proof, proof_height))
56}
57
58/// Queries for all existing IBC channels and returns the corresponding channel ends
59pub fn query_channels<I>(
60    ibc_ctx: &I,
61    _request: &QueryChannelsRequest,
62) -> Result<QueryChannelsResponse, QueryError>
63where
64    I: QueryContext,
65{
66    let channel_ends = ibc_ctx.channel_ends()?;
67
68    Ok(QueryChannelsResponse::new(
69        channel_ends,
70        ibc_ctx.host_height()?,
71        None,
72    ))
73}
74
75/// Queries for all channels associated with a given connection
76pub fn query_connection_channels<I>(
77    ibc_ctx: &I,
78    request: &QueryConnectionChannelsRequest,
79) -> Result<QueryConnectionChannelsResponse, QueryError>
80where
81    I: QueryContext,
82{
83    let all_channel_ends = ibc_ctx.channel_ends()?;
84
85    let connection_channel_ends = all_channel_ends
86        .into_iter()
87        .filter(|channel_end| {
88            channel_end
89                .channel_end
90                .connection_hops()
91                .iter()
92                .any(|connection_hop| connection_hop == &request.connection_id)
93        })
94        .map(Into::into)
95        .collect();
96
97    Ok(QueryConnectionChannelsResponse::new(
98        connection_channel_ends,
99        ibc_ctx.host_height()?,
100        None,
101    ))
102}
103
104/// Queries for the client state associated with a channel by the given channel
105/// and port ids
106pub fn query_channel_client_state<I>(
107    ibc_ctx: &I,
108    request: &QueryChannelClientStateRequest,
109) -> Result<QueryChannelClientStateResponse, QueryError>
110where
111    I: QueryContext,
112{
113    let channel_end_path = ChannelEndPath::new(&request.port_id, &request.channel_id);
114
115    let channel_end = ibc_ctx.channel_end(&channel_end_path)?;
116
117    let connection_end = channel_end
118        .connection_hops()
119        .first()
120        .map(|connection_id| ibc_ctx.connection_end(connection_id))
121        .ok_or_else(|| {
122            QueryError::missing_proof(format!(
123                "Channel {} does not have a connection",
124                request.channel_id
125            ))
126        })??;
127
128    let client_val_ctx = ibc_ctx.get_client_validation_context();
129
130    let client_state = client_val_ctx.client_state(connection_end.client_id())?;
131
132    let proof_height = match request.query_height {
133        Some(height) => height,
134        None => ibc_ctx.host_height()?,
135    };
136
137    let proof = ibc_ctx
138        .get_proof(
139            proof_height,
140            &Path::ClientState(ClientStatePath::new(connection_end.client_id().clone())),
141        )
142        .ok_or_else(|| {
143            QueryError::missing_proof(format!(
144                "Proof not found for client state path: {:?}",
145                connection_end.client_id()
146            ))
147        })?;
148
149    Ok(QueryChannelClientStateResponse::new(
150        IdentifiedClientState::new(connection_end.client_id().clone(), client_state.into()),
151        proof,
152        proof_height,
153    ))
154}
155
156/// Queries for the consensus state associated with a channel by the given
157/// target height, channel and port ids
158pub fn query_channel_consensus_state<I>(
159    ibc_ctx: &I,
160    request: &QueryChannelConsensusStateRequest,
161) -> Result<QueryChannelConsensusStateResponse, QueryError>
162where
163    I: QueryContext,
164    ConsensusStateRef<I>: Into<Any>,
165{
166    let channel_end_path = ChannelEndPath::new(&request.port_id, &request.channel_id);
167
168    let channel_end = ibc_ctx.channel_end(&channel_end_path)?;
169
170    let connection_end = channel_end
171        .connection_hops()
172        .first()
173        .map(|connection_id| ibc_ctx.connection_end(connection_id))
174        .ok_or_else(|| {
175            QueryError::missing_proof(format!(
176                "Channel {} does not have a connection",
177                request.channel_id
178            ))
179        })??;
180
181    let consensus_path = ClientConsensusStatePath::new(
182        connection_end.client_id().clone(),
183        request.consensus_height.revision_number(),
184        request.consensus_height.revision_height(),
185    );
186    let client_val_ctx = ibc_ctx.get_client_validation_context();
187
188    let consensus_state = client_val_ctx.consensus_state(&consensus_path)?;
189
190    let proof_height = match request.query_height {
191        Some(height) => height,
192        None => ibc_ctx.host_height()?,
193    };
194
195    let proof = ibc_ctx
196        .get_proof(
197            proof_height,
198            &Path::ClientConsensusState(consensus_path.clone()),
199        )
200        .ok_or_else(|| {
201            QueryError::missing_proof(format!(
202                "Proof not found for client consensus state path: {consensus_path:?}"
203            ))
204        })?;
205
206    Ok(QueryChannelConsensusStateResponse::new(
207        consensus_state.into(),
208        connection_end.client_id().clone(),
209        proof,
210        proof_height,
211    ))
212}
213
214/// Queries for the packet commitment associated with a channel by the given
215/// sequence, channel, and port ids
216pub fn query_packet_commitment<I>(
217    ibc_ctx: &I,
218    request: &QueryPacketCommitmentRequest,
219) -> Result<QueryPacketCommitmentResponse, QueryError>
220where
221    I: ValidationContext + ProvableContext,
222{
223    let commitment_path =
224        CommitmentPath::new(&request.port_id, &request.channel_id, request.sequence);
225
226    let packet_commitment_data = ibc_ctx.get_packet_commitment(&commitment_path)?;
227
228    let proof_height = match request.query_height {
229        Some(height) => height,
230        None => ibc_ctx.host_height()?,
231    };
232
233    let proof = ibc_ctx
234        .get_proof(proof_height, &Path::Commitment(commitment_path.clone()))
235        .ok_or_else(|| {
236            QueryError::missing_proof(format!(
237                "Proof not found for packet commitment path: {commitment_path:?}"
238            ))
239        })?;
240
241    Ok(QueryPacketCommitmentResponse::new(
242        packet_commitment_data,
243        proof,
244        proof_height,
245    ))
246}
247
248/// Queries for all packet commitments associated with a channel
249pub fn query_packet_commitments<I>(
250    ibc_ctx: &I,
251    request: &QueryPacketCommitmentsRequest,
252) -> Result<QueryPacketCommitmentsResponse, QueryError>
253where
254    I: QueryContext,
255{
256    let channel_end_path = ChannelEndPath::new(&request.port_id, &request.channel_id);
257
258    let commitments = ibc_ctx
259        .packet_commitments(&channel_end_path)?
260        .into_iter()
261        .map(Into::into)
262        .collect();
263
264    Ok(QueryPacketCommitmentsResponse::new(
265        commitments,
266        ibc_ctx.host_height()?,
267        None,
268    ))
269}
270
271/// Queries for the packet receipt associated with a channel by the given
272/// sequence, channel, and port ids
273pub fn query_packet_receipt<I>(
274    ibc_ctx: &I,
275    request: &QueryPacketReceiptRequest,
276) -> Result<QueryPacketReceiptResponse, QueryError>
277where
278    I: ValidationContext + ProvableContext,
279{
280    let receipt_path = ReceiptPath::new(&request.port_id, &request.channel_id, request.sequence);
281
282    // Unreceived packets are not stored
283    let packet_receipt_data = ibc_ctx.get_packet_receipt(&receipt_path)?;
284
285    let proof_height = match request.query_height {
286        Some(height) => height,
287        None => ibc_ctx.host_height()?,
288    };
289
290    let proof = ibc_ctx
291        .get_proof(proof_height, &Path::Receipt(receipt_path.clone()))
292        .ok_or_else(|| {
293            QueryError::missing_proof(format!(
294                "Proof not found for packet receipt path: {receipt_path:?}"
295            ))
296        })?;
297
298    Ok(QueryPacketReceiptResponse::new(
299        packet_receipt_data.is_ok(),
300        proof,
301        proof_height,
302    ))
303}
304
305/// Queries for the packet acknowledgement associated with a channel by the
306/// given sequence, channel, and port ids
307pub fn query_packet_acknowledgement<I>(
308    ibc_ctx: &I,
309    request: &QueryPacketAcknowledgementRequest,
310) -> Result<QueryPacketAcknowledgementResponse, QueryError>
311where
312    I: ValidationContext + ProvableContext,
313{
314    let acknowledgement_path =
315        AckPath::new(&request.port_id, &request.channel_id, request.sequence);
316
317    let packet_acknowledgement_data = ibc_ctx.get_packet_acknowledgement(&acknowledgement_path)?;
318
319    let proof_height = match request.query_height {
320        Some(height) => height,
321        None => ibc_ctx.host_height()?,
322    };
323
324    let proof = ibc_ctx
325        .get_proof(proof_height, &Path::Ack(acknowledgement_path.clone()))
326        .ok_or_else(|| {
327            QueryError::missing_proof(format!(
328                "Proof not found for packet acknowledgement path: {acknowledgement_path:?}"
329            ))
330        })?;
331
332    Ok(QueryPacketAcknowledgementResponse::new(
333        packet_acknowledgement_data,
334        proof,
335        proof_height,
336    ))
337}
338
339/// Queries for all packet acknowledgements associated with a channel
340pub fn query_packet_acknowledgements<I>(
341    ibc_ctx: &I,
342    request: &QueryPacketAcknowledgementsRequest,
343) -> Result<QueryPacketAcknowledgementsResponse, QueryError>
344where
345    I: QueryContext,
346{
347    let commitment_sequences = request
348        .packet_commitment_sequences
349        .iter()
350        .copied()
351        .map(Into::into);
352
353    let channel_end_path = ChannelEndPath::new(&request.port_id, &request.channel_id);
354
355    let acknowledgements = ibc_ctx
356        .packet_acknowledgements(&channel_end_path, commitment_sequences)?
357        .into_iter()
358        .map(Into::into)
359        .collect();
360
361    Ok(QueryPacketAcknowledgementsResponse::new(
362        acknowledgements,
363        ibc_ctx.host_height()?,
364        None,
365    ))
366}
367
368/// Queries for all unreceived packets associated with a channel
369pub fn query_unreceived_packets<I>(
370    ibc_ctx: &I,
371    request: &QueryUnreceivedPacketsRequest,
372) -> Result<QueryUnreceivedPacketsResponse, QueryError>
373where
374    I: QueryContext,
375{
376    let sequences = request
377        .packet_commitment_sequences
378        .iter()
379        .copied()
380        .map(Into::into);
381
382    let channel_end_path = ChannelEndPath::new(&request.port_id, &request.channel_id);
383
384    let unreceived_packets = ibc_ctx.unreceived_packets(&channel_end_path, sequences)?;
385
386    Ok(QueryUnreceivedPacketsResponse::new(
387        unreceived_packets,
388        ibc_ctx.host_height()?,
389    ))
390}
391
392/// Queries for all unreceived acknowledgements associated with a channel
393pub fn query_unreceived_acks<I>(
394    ibc_ctx: &I,
395    request: &QueryUnreceivedAcksRequest,
396) -> Result<QueryUnreceivedAcksResponse, QueryError>
397where
398    I: QueryContext,
399{
400    let sequences = request.packet_ack_sequences.iter().copied().map(Into::into);
401
402    let channel_end_path = ChannelEndPath::new(&request.port_id, &request.channel_id);
403
404    let unreceived_acks = ibc_ctx.unreceived_acks(&channel_end_path, sequences)?;
405
406    Ok(QueryUnreceivedAcksResponse::new(
407        unreceived_acks,
408        ibc_ctx.host_height()?,
409    ))
410}
411
412/// Queries for the next sequence to send for the channel specified
413/// in the `request`.
414pub fn query_next_sequence_send<I>(
415    ibc_ctx: &I,
416    request: &QueryNextSequenceSendRequest,
417) -> Result<QueryNextSequenceSendResponse, QueryError>
418where
419    I: ValidationContext + ProvableContext,
420{
421    let next_seq_send_path = SeqSendPath::new(&request.port_id, &request.channel_id);
422
423    let next_sequence_send = ibc_ctx.get_next_sequence_send(&next_seq_send_path)?;
424
425    let proof_height = match request.query_height {
426        Some(height) => height,
427        None => ibc_ctx.host_height()?,
428    };
429
430    let proof = ibc_ctx
431        .get_proof(proof_height, &Path::SeqSend(next_seq_send_path))
432        .ok_or_else(|| {
433            QueryError::missing_proof(format!(
434                "Next sequence send proof not found for channel {}",
435                request.channel_id
436            ))
437        })?;
438
439    Ok(QueryNextSequenceSendResponse::new(
440        next_sequence_send,
441        proof,
442        proof_height,
443    ))
444}
445
446/// Queries for the next sequence receive associated with a channel
447pub fn query_next_sequence_receive<I>(
448    ibc_ctx: &I,
449    request: &QueryNextSequenceReceiveRequest,
450) -> Result<QueryNextSequenceReceiveResponse, QueryError>
451where
452    I: ValidationContext + ProvableContext,
453{
454    let next_seq_recv_path = SeqRecvPath::new(&request.port_id, &request.channel_id);
455
456    let next_sequence_recv = ibc_ctx.get_next_sequence_recv(&next_seq_recv_path)?;
457
458    let proof_height = match request.query_height {
459        Some(height) => height,
460        None => ibc_ctx.host_height()?,
461    };
462
463    let proof = ibc_ctx
464        .get_proof(proof_height, &Path::SeqRecv(next_seq_recv_path))
465        .ok_or_else(|| {
466            QueryError::missing_proof(format!(
467                "Next sequence receive proof not found for channel {}",
468                request.channel_id
469            ))
470        })?;
471
472    Ok(QueryNextSequenceReceiveResponse::new(
473        next_sequence_recv,
474        proof,
475        proof_height,
476    ))
477}