ibc_query/core/client/
query.rs

1//! Provides utility functions for querying IBC client states.
2
3use ibc::core::client::context::client_state::ClientStateValidation;
4use ibc::core::client::context::ClientValidationContext;
5use ibc::core::client::types::error::ClientError;
6use ibc::core::host::types::path::{
7    ClientConsensusStatePath, ClientStatePath, Path, UpgradeClientStatePath,
8    UpgradeConsensusStatePath, UPGRADED_IBC_STATE,
9};
10use ibc::core::host::{ConsensusStateRef, ValidationContext};
11use ibc::cosmos_host::upgrade_proposal::{UpgradeValidationContext, UpgradedConsensusStateRef};
12use ibc::primitives::prelude::{format, ToString};
13use ibc::primitives::proto::Any;
14
15use super::{
16    ConsensusStateWithHeight, IdentifiedClientState, QueryClientStateResponse,
17    QueryClientStatesRequest, QueryClientStatesResponse, QueryClientStatusRequest,
18    QueryClientStatusResponse, QueryConsensusStateHeightsRequest,
19    QueryConsensusStateHeightsResponse, QueryConsensusStateRequest, QueryConsensusStateResponse,
20    QueryConsensusStatesRequest, QueryConsensusStatesResponse, QueryUpgradedClientStateRequest,
21    QueryUpgradedClientStateResponse, QueryUpgradedConsensusStateRequest,
22    QueryUpgradedConsensusStateResponse,
23};
24use crate::core::client::QueryClientStateRequest;
25use crate::core::context::{ProvableContext, QueryContext};
26use crate::error::QueryError;
27
28/// Queries for the client state of a given client id.
29pub fn query_client_state<I>(
30    ibc_ctx: &I,
31    request: &QueryClientStateRequest,
32) -> Result<QueryClientStateResponse, QueryError>
33where
34    I: QueryContext,
35{
36    let client_id = request.client_id.clone();
37
38    let client_val_ctx = ibc_ctx.get_client_validation_context();
39
40    let client_state = client_val_ctx.client_state(&client_id)?;
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(
49            proof_height,
50            &Path::ClientState(ClientStatePath::new(client_id.clone())),
51        )
52        .ok_or_else(|| {
53            QueryError::missing_proof(format!(
54                "Proof not found for client state path: {client_id:?}"
55            ))
56        })?;
57
58    Ok(QueryClientStateResponse::new(
59        client_state.into(),
60        proof,
61        proof_height,
62    ))
63}
64
65/// Queries for all the existing client states.
66pub fn query_client_states<I>(
67    ibc_ctx: &I,
68    _request: &QueryClientStatesRequest,
69) -> Result<QueryClientStatesResponse, QueryError>
70where
71    I: QueryContext,
72{
73    let client_states = ibc_ctx.client_states()?;
74
75    Ok(QueryClientStatesResponse::new(
76        client_states
77            .into_iter()
78            .map(|(id, state)| IdentifiedClientState::new(id, state.into()))
79            .collect(),
80        // no support for pagination yet
81        None,
82    ))
83}
84
85/// Queries for the consensus state of a given client id and height.
86pub fn query_consensus_state<I>(
87    ibc_ctx: &I,
88    request: &QueryConsensusStateRequest,
89) -> Result<QueryConsensusStateResponse, QueryError>
90where
91    I: QueryContext,
92    ConsensusStateRef<I>: Into<Any>,
93{
94    let client_id = request.client_id.clone();
95
96    let (height, consensus_state) = if let Some(height) = request.consensus_height {
97        let client_val_ctx = ibc_ctx.get_client_validation_context();
98
99        let consensus_state = client_val_ctx.consensus_state(&ClientConsensusStatePath::new(
100            client_id.clone(),
101            height.revision_number(),
102            height.revision_height(),
103        ))?;
104
105        (height, consensus_state)
106    } else {
107        ibc_ctx
108            .consensus_states(&client_id)?
109            .into_iter()
110            .max_by_key(|&(h, _)| h)
111            .ok_or_else(|| {
112                QueryError::missing_proof(format!(
113                    "No consensus state found for client: {client_id:?}"
114                ))
115            })?
116    };
117
118    let proof_height = match request.query_height {
119        Some(height) => height,
120        None => ibc_ctx.host_height()?,
121    };
122
123    let proof = ibc_ctx
124        .get_proof(
125            proof_height,
126            &Path::ClientConsensusState(ClientConsensusStatePath::new(
127                client_id.clone(),
128                height.revision_number(),
129                height.revision_height(),
130            )),
131        )
132        .ok_or_else(|| {
133            QueryError::missing_proof(format!(
134                "Proof not found for consensus state path: {client_id:?}"
135            ))
136        })?;
137
138    Ok(QueryConsensusStateResponse::new(
139        consensus_state.into(),
140        proof,
141        proof_height,
142    ))
143}
144
145/// Queries for all the consensus states of a given client id.
146pub fn query_consensus_states<I>(
147    ibc_ctx: &I,
148    request: &QueryConsensusStatesRequest,
149) -> Result<QueryConsensusStatesResponse, QueryError>
150where
151    I: QueryContext,
152    ConsensusStateRef<I>: Into<Any>,
153{
154    let consensus_states = ibc_ctx.consensus_states(&request.client_id)?;
155
156    Ok(QueryConsensusStatesResponse::new(
157        consensus_states
158            .into_iter()
159            .map(|(height, state)| ConsensusStateWithHeight::new(height, state.into()))
160            .collect(),
161        // no support for pagination yet,
162        None,
163    ))
164}
165
166/// Queries for the heights of all the consensus states of a given client id.
167pub fn query_consensus_state_heights<I>(
168    ibc_ctx: &I,
169    request: &QueryConsensusStateHeightsRequest,
170) -> Result<QueryConsensusStateHeightsResponse, QueryError>
171where
172    I: QueryContext,
173{
174    let consensus_state_heights = ibc_ctx.consensus_state_heights(&request.client_id)?;
175
176    Ok(QueryConsensusStateHeightsResponse::new(
177        consensus_state_heights,
178        // no support for pagination yet
179        None,
180    ))
181}
182
183/// Queries for the status (Active, Frozen, Expired, Unauthorized) of a given client.
184pub fn query_client_status<I>(
185    ibc_ctx: &I,
186    request: &QueryClientStatusRequest,
187) -> Result<QueryClientStatusResponse, QueryError>
188where
189    I: ValidationContext,
190{
191    let client_val_ctx = ibc_ctx.get_client_validation_context();
192    let client_state = client_val_ctx.client_state(&request.client_id)?;
193    let client_validation_ctx = ibc_ctx.get_client_validation_context();
194    let client_status = client_state.status(client_validation_ctx, &request.client_id)?;
195
196    Ok(QueryClientStatusResponse::new(client_status))
197}
198
199/// Queries for the upgraded client state.
200pub fn query_upgraded_client_state<I, U>(
201    ibc_ctx: &I,
202    upgrade_ctx: &U,
203    request: &QueryUpgradedClientStateRequest,
204) -> Result<QueryUpgradedClientStateResponse, QueryError>
205where
206    I: ValidationContext,
207    U: UpgradeValidationContext + ProvableContext,
208{
209    let upgrade_path = match &request.upgrade_path {
210        Some(path) if !path.is_empty() => path,
211        _ => UPGRADED_IBC_STATE,
212    }
213    .to_string();
214
215    let upgrade_revision_height = match request.upgrade_height {
216        Some(height) => height.revision_height(),
217        None => {
218            upgrade_ctx
219                .upgrade_plan()
220                .map_err(ClientError::from)?
221                .height
222        }
223    };
224
225    let upgraded_client_state_path = UpgradeClientStatePath {
226        upgrade_path,
227        height: upgrade_revision_height,
228    };
229
230    let upgraded_client_state = upgrade_ctx
231        .upgraded_client_state(&upgraded_client_state_path)
232        .map_err(ClientError::from)?;
233
234    let proof_height = match request.query_height {
235        Some(height) => height,
236        None => ibc_ctx.host_height()?,
237    };
238
239    let proof = upgrade_ctx
240        .get_proof(
241            proof_height,
242            &Path::UpgradeClientState(upgraded_client_state_path),
243        )
244        .ok_or_else(|| {
245            QueryError::missing_proof(format!(
246                "Proof not found for upgraded client state at: {proof_height:?}"
247            ))
248        })?;
249
250    Ok(QueryUpgradedClientStateResponse::new(
251        upgraded_client_state.into(),
252        proof,
253        proof_height,
254    ))
255}
256
257/// Queries for the upgraded consensus state.
258pub fn query_upgraded_consensus_state<I, U>(
259    ibc_ctx: &I,
260    upgrade_ctx: &U,
261    request: &QueryUpgradedConsensusStateRequest,
262) -> Result<QueryUpgradedConsensusStateResponse, QueryError>
263where
264    I: ValidationContext,
265    U: UpgradeValidationContext + ProvableContext,
266    UpgradedConsensusStateRef<U>: Into<Any>,
267{
268    let upgrade_path = match &request.upgrade_path {
269        Some(path) if !path.is_empty() => path,
270        _ => UPGRADED_IBC_STATE,
271    }
272    .to_string();
273
274    let upgrade_revision_height = match request.upgrade_height {
275        Some(height) => height.revision_height(),
276        None => {
277            upgrade_ctx
278                .upgrade_plan()
279                .map_err(ClientError::from)?
280                .height
281        }
282    };
283
284    let upgraded_consensus_state_path = UpgradeConsensusStatePath {
285        upgrade_path,
286        height: upgrade_revision_height,
287    };
288
289    let upgraded_consensus_state = upgrade_ctx
290        .upgraded_consensus_state(&upgraded_consensus_state_path)
291        .map_err(ClientError::from)?;
292
293    let proof_height = match request.query_height {
294        Some(height) => height,
295        None => ibc_ctx.host_height()?,
296    };
297
298    let proof = upgrade_ctx
299        .get_proof(
300            proof_height,
301            &Path::UpgradeConsensusState(upgraded_consensus_state_path),
302        )
303        .ok_or_else(|| {
304            QueryError::missing_proof(format!(
305                "Proof not found for upgraded consensus state at: {proof_height:?}"
306            ))
307        })?;
308
309    Ok(QueryUpgradedConsensusStateResponse::new(
310        upgraded_consensus_state.into(),
311        proof,
312        proof_height,
313    ))
314}