From a4a5f78405bd3a28c060df6c65438edf0b02e9e2 Mon Sep 17 00:00:00 2001 From: Farhad Shabani Date: Wed, 4 Oct 2023 11:58:28 -0700 Subject: [PATCH] imp: enhance IBC query methods usability and code organization (#897) * imp: enhance IBC query methods usability and code organization * docs: update contributing.md * fix: include ibc-query in the workspace * docs: add docstring for query modules --- ...896-enhance-ibc-query-methods-usability.md | 4 + CONTRIBUTING.md | 19 +- Cargo.toml | 1 + crates/ibc-query/Cargo.toml | 24 + crates/ibc-query/LICENSE | 202 +++++++ crates/ibc-query/README.md | 7 + crates/ibc-query/src/core/channel/mod.rs | 5 + crates/ibc-query/src/core/channel/query.rs | 538 +++++++++++++++++ crates/ibc-query/src/core/channel/service.rs | 196 ++++++ crates/ibc-query/src/core/client/mod.rs | 5 + crates/ibc-query/src/core/client/query.rs | 240 ++++++++ crates/ibc-query/src/core/client/service.rs | 156 +++++ crates/ibc-query/src/core/connection/mod.rs | 5 + crates/ibc-query/src/core/connection/query.rs | 195 ++++++ .../ibc-query/src/core/connection/service.rs | 112 ++++ .../src}/core/context.rs | 16 +- .../services => ibc-query/src}/core/mod.rs | 0 crates/ibc-query/src/error.rs | 46 ++ .../services/mod.rs => ibc-query/src/lib.rs} | 4 +- crates/ibc/Cargo.toml | 3 - crates/ibc/src/lib.rs | 5 +- crates/ibc/src/services/core/channel.rs | 559 ------------------ crates/ibc/src/services/core/client.rs | 298 ---------- crates/ibc/src/services/core/connection.rs | 227 ------- crates/ibc/src/services/error.rs | 18 - 25 files changed, 1758 insertions(+), 1127 deletions(-) create mode 100644 .changelog/unreleased/improvements/896-enhance-ibc-query-methods-usability.md create mode 100644 crates/ibc-query/Cargo.toml create mode 100644 crates/ibc-query/LICENSE create mode 100644 crates/ibc-query/README.md create mode 100644 crates/ibc-query/src/core/channel/mod.rs create mode 100644 crates/ibc-query/src/core/channel/query.rs create mode 100644 crates/ibc-query/src/core/channel/service.rs create mode 100644 crates/ibc-query/src/core/client/mod.rs create mode 100644 crates/ibc-query/src/core/client/query.rs create mode 100644 crates/ibc-query/src/core/client/service.rs create mode 100644 crates/ibc-query/src/core/connection/mod.rs create mode 100644 crates/ibc-query/src/core/connection/query.rs create mode 100644 crates/ibc-query/src/core/connection/service.rs rename crates/{ibc/src/services => ibc-query/src}/core/context.rs (87%) rename crates/{ibc/src/services => ibc-query/src}/core/mod.rs (100%) create mode 100644 crates/ibc-query/src/error.rs rename crates/{ibc/src/services/mod.rs => ibc-query/src/lib.rs} (95%) delete mode 100644 crates/ibc/src/services/core/channel.rs delete mode 100644 crates/ibc/src/services/core/client.rs delete mode 100644 crates/ibc/src/services/core/connection.rs delete mode 100644 crates/ibc/src/services/error.rs diff --git a/.changelog/unreleased/improvements/896-enhance-ibc-query-methods-usability.md b/.changelog/unreleased/improvements/896-enhance-ibc-query-methods-usability.md new file mode 100644 index 000000000..0f785755d --- /dev/null +++ b/.changelog/unreleased/improvements/896-enhance-ibc-query-methods-usability.md @@ -0,0 +1,4 @@ +- Enhance IBC query methods usability and code organization + - The implementation of query methods is now publicly accessible as standalone functions. + - `grpc` feature now lives as a separate crate called as `ibc-query` + ([#896](https://github.com/cosmos/ibc-rs/issues/896)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a73a2d289..d7429ccfc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -254,8 +254,8 @@ Our release process is as follows: 3. If there were changes in the `ibc-derive` crate, we need to publish a new version of that crate. 1. bump the version in `crates/ibc-derive/Cargo.toml` 2. Publish `ibc-derive` with `cargo publish -p ibc-derive` -4. Bump all relevant versions in `crates/ibc/Cargo.toml` to the new version and - push these changes to the release PR. +4. Bump the versions of relevant crates (`ibc` and `ibc-query`) in their + `Cargo.toml` to the new version, and push these changes to the release PR. + If you released a new version of `ibc-derive` in step 3, make sure to update that dependency. 5. Run `cargo doc -p ibc --all-features --open` locally to double-check that all the documentation compiles and seems up-to-date and coherent. Fix any potential @@ -264,12 +264,13 @@ Our release process is as follows: any potential issues here and push them to the release PR. 7. Mark the PR as **Ready for Review** and incorporate feedback on the release. 8. Once approved, merge the PR, and pull the `main` branch. -9. Run `cargo publish -p ibc` -9. Create a signed tag and push it to GitHub: `git tag -s -a vX.Y.Z`. In the tag - message, write the version and the link to the corresponding section of the - changelog. - + Push the tag with `git push --tags` -10. Once the tag is pushed, create a GitHub release and append +9. Run `cargo publish -p ibc` and `cargo publish -p ibc-query` to publish the crates. +10. Create a signed tag and push it to GitHub: `git tag -s -a vX.Y.Z`. In the + tag message, write the version and the link to the corresponding section of + the changelog. + + Push the tag with `git push --tags` +11. Once the tag is pushed, create a GitHub release and append `[📖CHANGELOG](https://github.com/cosmos/ibc-rs/blob/main/CHANGELOG.md#vXYZ)` to the release description. -11. All done! 🎉 + +All done! 🎉 diff --git a/Cargo.toml b/Cargo.toml index 9e05bd35d..d1fa2ad83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ resolver = "2" members = [ "crates/ibc", "crates/ibc-derive", + "crates/ibc-query", ] exclude = [ diff --git a/crates/ibc-query/Cargo.toml b/crates/ibc-query/Cargo.toml new file mode 100644 index 000000000..672928281 --- /dev/null +++ b/crates/ibc-query/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "ibc-query" +version = "0.45.0" +edition = "2021" +license = "Apache-2.0" +readme = "README.md" +keywords = ["blockchain", "cosmos", "ibc", "rpc", "grpc"] +repository = "https://github.com/cosmos/ibc-rs" +authors = ["Informal Systems "] +rust-version = "1.60" +description = """ + 'ibc-query' contains essential IBC query methods and + gRPC query service implementations for the IBC core. +""" + +[features] +default = ["std"] +std = ["ibc-proto/std", "ibc/std"] + +[dependencies] +ibc = { version = "0.45.0", path = "../ibc", default-features = false } +ibc-proto = { version = "0.35.0", default-features = false, features = ["server"] } +displaydoc = { version = "0.2", default-features = false } +tonic = "0.9" \ No newline at end of file diff --git a/crates/ibc-query/LICENSE b/crates/ibc-query/LICENSE new file mode 100644 index 000000000..58b61ab0f --- /dev/null +++ b/crates/ibc-query/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020 Informal Systems + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/crates/ibc-query/README.md b/crates/ibc-query/README.md new file mode 100644 index 000000000..346b79544 --- /dev/null +++ b/crates/ibc-query/README.md @@ -0,0 +1,7 @@ +# ibc-query + +This crate provides a set of utility traits and implementations for querying the +state of an IBC-enabled chain. + +Implementations contains essential IBC query methods and gRPC query service +for the IBC core client, connection, and channel components. diff --git a/crates/ibc-query/src/core/channel/mod.rs b/crates/ibc-query/src/core/channel/mod.rs new file mode 100644 index 000000000..b24e36180 --- /dev/null +++ b/crates/ibc-query/src/core/channel/mod.rs @@ -0,0 +1,5 @@ +mod query; +mod service; + +pub use query::*; +pub use service::*; diff --git a/crates/ibc-query/src/core/channel/query.rs b/crates/ibc-query/src/core/channel/query.rs new file mode 100644 index 000000000..48eef8463 --- /dev/null +++ b/crates/ibc-query/src/core/channel/query.rs @@ -0,0 +1,538 @@ +//! Provides utility functions for querying IBC channel states. + +use alloc::format; +use core::str::FromStr; + +use ibc::core::ics04_channel::packet::Sequence; +use ibc::core::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; +use ibc::core::ics24_host::path::{ + AckPath, ChannelEndPath, ClientConsensusStatePath, ClientStatePath, CommitmentPath, Path, + ReceiptPath, SeqRecvPath, SeqSendPath, +}; +use ibc::core::ValidationContext; +use ibc::Height; +use ibc_proto::google::protobuf::Any; +use ibc_proto::ibc::core::channel::v1::{ + PacketState, QueryChannelClientStateRequest, QueryChannelClientStateResponse, + QueryChannelConsensusStateRequest, QueryChannelConsensusStateResponse, QueryChannelRequest, + QueryChannelResponse, QueryChannelsRequest, QueryChannelsResponse, + QueryConnectionChannelsRequest, QueryConnectionChannelsResponse, + QueryNextSequenceReceiveRequest, QueryNextSequenceReceiveResponse, + QueryNextSequenceSendRequest, QueryNextSequenceSendResponse, QueryPacketAcknowledgementRequest, + QueryPacketAcknowledgementResponse, QueryPacketAcknowledgementsRequest, + QueryPacketAcknowledgementsResponse, QueryPacketCommitmentRequest, + QueryPacketCommitmentResponse, QueryPacketCommitmentsRequest, QueryPacketCommitmentsResponse, + QueryPacketReceiptRequest, QueryPacketReceiptResponse, QueryUnreceivedAcksRequest, + QueryUnreceivedAcksResponse, QueryUnreceivedPacketsRequest, QueryUnreceivedPacketsResponse, +}; +use ibc_proto::ibc::core::client::v1::IdentifiedClientState; + +use crate::core::context::{ProvableContext, QueryContext}; +use crate::error::QueryError; + +/// Queries for a specific IBC channel by the given channel and port ids and +/// returns the channel end with the associated proof. +pub fn query_channel( + ibc_ctx: &I, + request: &QueryChannelRequest, +) -> Result +where + I: ValidationContext + ProvableContext, +{ + let channel_id = ChannelId::from_str(request.channel_id.as_str())?; + + let port_id = PortId::from_str(request.port_id.as_str())?; + + let channel_end_path = ChannelEndPath::new(&port_id, &channel_id); + + let channel_end = ibc_ctx.channel_end(&channel_end_path)?; + + let current_height = ibc_ctx.host_height()?; + let proof = ibc_ctx + .get_proof(current_height, &Path::ChannelEnd(channel_end_path.clone())) + .ok_or(QueryError::ProofNotFound { + description: format!( + "Proof not found for channel end path {:?}", + channel_end_path + ), + })?; + + Ok(QueryChannelResponse { + channel: Some(channel_end.into()), + proof, + proof_height: Some(current_height.into()), + }) +} + +/// Queries for all existing IBC channels and returns the corresponding channel ends +pub fn query_channels( + ibc_ctx: &I, + _request: &QueryChannelsRequest, +) -> Result +where + I: QueryContext, +{ + let channel_ends = ibc_ctx.channel_ends()?; + + Ok(QueryChannelsResponse { + channels: channel_ends.into_iter().map(Into::into).collect(), + height: Some(ibc_ctx.host_height()?.into()), + // no support for pagination yet + pagination: None, + }) +} + +/// Queries for all channels associated with a given connection +pub fn query_connection_channels( + ibc_ctx: &I, + request: &QueryConnectionChannelsRequest, +) -> Result +where + I: QueryContext, +{ + let connection_id = ConnectionId::from_str(request.connection.as_str())?; + + let all_channel_ends = ibc_ctx.channel_ends()?; + + let connection_channel_ends = all_channel_ends + .into_iter() + .filter(|channel_end| { + channel_end + .channel_end + .connection_hops() + .iter() + .any(|connection_hop| connection_hop == &connection_id) + }) + .map(Into::into) + .collect(); + + Ok(QueryConnectionChannelsResponse { + channels: connection_channel_ends, + height: Some(ibc_ctx.host_height()?.into()), + // no support for pagination yet + pagination: None, + }) +} + +/// Queries for the client state associated with a channel by the given channel +/// and port ids +pub fn query_channel_client_state( + ibc_ctx: &I, + request: &QueryChannelClientStateRequest, +) -> Result +where + I: QueryContext, + ::AnyClientState: Into, +{ + let channel_id = ChannelId::from_str(request.channel_id.as_str())?; + + let port_id = PortId::from_str(request.port_id.as_str())?; + + let channel_end_path = ChannelEndPath::new(&port_id, &channel_id); + + let channel_end = ibc_ctx.channel_end(&channel_end_path)?; + + let connection_end = channel_end + .connection_hops() + .first() + .map(|connection_id| ibc_ctx.connection_end(connection_id)) + .ok_or(QueryError::ProofNotFound { + description: format!("Channel {} does not have a connection", channel_id), + })??; + + let client_state = ibc_ctx.client_state(connection_end.client_id())?; + + let current_height = ibc_ctx.host_height()?; + + let proof = ibc_ctx + .get_proof( + current_height, + &Path::ClientState(ClientStatePath::new(connection_end.client_id())), + ) + .ok_or(QueryError::ProofNotFound { + description: format!( + "Proof not found for client state path: {:?}", + connection_end.client_id() + ), + })?; + + Ok(QueryChannelClientStateResponse { + identified_client_state: Some(IdentifiedClientState { + client_id: connection_end.client_id().as_str().into(), + client_state: Some(client_state.into()), + }), + proof, + proof_height: Some(current_height.into()), + }) +} + +/// Queries for the consensus state associated with a channel by the given +/// target height, channel and port ids +pub fn query_channel_consensus_state( + ibc_ctx: &I, + request: &QueryChannelConsensusStateRequest, +) -> Result +where + I: QueryContext, + ::AnyConsensusState: Into, +{ + let channel_id = ChannelId::from_str(request.channel_id.as_str())?; + + let port_id = PortId::from_str(request.port_id.as_str())?; + + let height = Height::new(request.revision_number, request.revision_height)?; + + let channel_end_path = ChannelEndPath::new(&port_id, &channel_id); + + let channel_end = ibc_ctx.channel_end(&channel_end_path)?; + + let connection_end = channel_end + .connection_hops() + .first() + .map(|connection_id| ibc_ctx.connection_end(connection_id)) + .ok_or(QueryError::ProofNotFound { + description: format!("Channel {} does not have a connection", channel_id), + })??; + + let consensus_path = ClientConsensusStatePath::new(connection_end.client_id(), &height); + + let consensus_state = ibc_ctx.consensus_state(&consensus_path)?; + + let current_height = ibc_ctx.host_height()?; + + let proof = ibc_ctx + .get_proof( + current_height, + &Path::ClientConsensusState(consensus_path.clone()), + ) + .ok_or(QueryError::ProofNotFound { + description: format!( + "Proof not found for client consensus state path: {:?}", + consensus_path + ), + })?; + + Ok(QueryChannelConsensusStateResponse { + client_id: connection_end.client_id().as_str().into(), + consensus_state: Some(consensus_state.into()), + proof, + proof_height: Some(current_height.into()), + }) +} + +/// Queries for the packet commitment associated with a channel by the given +/// sequence, channel and port ids +pub fn query_packet_commitment( + ibc_ctx: &I, + request: &QueryPacketCommitmentRequest, +) -> Result +where + I: ValidationContext + ProvableContext, +{ + let channel_id = ChannelId::from_str(request.channel_id.as_str())?; + + let port_id = PortId::from_str(request.port_id.as_str())?; + + let sequence = Sequence::from(request.sequence); + + let commitment_path = CommitmentPath::new(&port_id, &channel_id, sequence); + + let packet_commitment_data = ibc_ctx.get_packet_commitment(&commitment_path)?; + + let current_height = ibc_ctx.host_height()?; + + let proof = ibc_ctx + .get_proof(current_height, &Path::Commitment(commitment_path.clone())) + .ok_or(QueryError::ProofNotFound { + description: format!( + "Proof not found for packet commitment path: {:?}", + commitment_path + ), + })?; + + Ok(QueryPacketCommitmentResponse { + commitment: packet_commitment_data.into_vec(), + proof, + proof_height: Some(current_height.into()), + }) +} + +/// Queries for all packet commitments associated with a channel +pub fn query_packet_commitments( + ibc_ctx: &I, + request: &QueryPacketCommitmentsRequest, +) -> Result +where + I: QueryContext, +{ + let channel_id = ChannelId::from_str(request.channel_id.as_str())?; + + let port_id = PortId::from_str(request.port_id.as_str())?; + + let channel_end_path = ChannelEndPath::new(&port_id, &channel_id); + + let commitments = ibc_ctx + .packet_commitments(&channel_end_path)? + .into_iter() + .map(|path| { + ibc_ctx + .get_packet_commitment(&path) + .map(|commitment| PacketState { + port_id: path.port_id.as_str().into(), + channel_id: path.channel_id.as_str().into(), + sequence: path.sequence.into(), + data: commitment.into_vec(), + }) + }) + .collect::>()?; + + Ok(QueryPacketCommitmentsResponse { + commitments, + height: Some(ibc_ctx.host_height()?.into()), + // no support for pagination yet + pagination: None, + }) +} + +/// Queries for the packet receipt associated with a channel by the given +/// sequence, channel and port ids +pub fn query_packet_receipt( + ibc_ctx: &I, + request: &QueryPacketReceiptRequest, +) -> Result +where + I: ValidationContext + ProvableContext, +{ + let channel_id = ChannelId::from_str(request.channel_id.as_str())?; + + let port_id = PortId::from_str(request.port_id.as_str())?; + + let sequence = Sequence::from(request.sequence); + + let receipt_path = ReceiptPath::new(&port_id, &channel_id, sequence); + + // Receipt only has one enum + // Unreceived packets are not stored + let packet_receipt_data = ibc_ctx.get_packet_receipt(&receipt_path); + + let current_height = ibc_ctx.host_height()?; + + let proof = ibc_ctx + .get_proof(current_height, &Path::Receipt(receipt_path.clone())) + .ok_or(QueryError::ProofNotFound { + description: format!( + "Proof not found for packet receipt path: {:?}", + receipt_path + ), + })?; + + Ok(QueryPacketReceiptResponse { + received: packet_receipt_data.is_ok(), + proof, + proof_height: Some(current_height.into()), + }) +} + +/// Queries for the packet acknowledgement associated with a channel by the +/// given sequence, channel and port ids +pub fn query_packet_acknowledgement( + ibc_ctx: &I, + request: &QueryPacketAcknowledgementRequest, +) -> Result +where + I: ValidationContext + ProvableContext, +{ + let channel_id = ChannelId::from_str(request.channel_id.as_str())?; + + let port_id = PortId::from_str(request.port_id.as_str())?; + + let sequence = Sequence::from(request.sequence); + + let acknowledgement_path = AckPath::new(&port_id, &channel_id, sequence); + + let packet_acknowledgement_data = ibc_ctx.get_packet_acknowledgement(&acknowledgement_path)?; + + let current_height = ibc_ctx.host_height()?; + + let proof = ibc_ctx + .get_proof(current_height, &Path::Ack(acknowledgement_path.clone())) + .ok_or(QueryError::ProofNotFound { + description: format!( + "Proof not found for packet acknowledgement path: {:?}", + acknowledgement_path + ), + })?; + + Ok(QueryPacketAcknowledgementResponse { + acknowledgement: packet_acknowledgement_data.into_vec(), + proof, + proof_height: Some(current_height.into()), + }) +} + +/// Queries for all packet acknowledgements associated with a channel +pub fn query_packet_acknowledgements( + ibc_ctx: &I, + request: &QueryPacketAcknowledgementsRequest, +) -> Result +where + I: QueryContext, +{ + let channel_id = ChannelId::from_str(request.channel_id.as_str())?; + + let port_id = PortId::from_str(request.port_id.as_str())?; + + let commitment_sequences = request + .packet_commitment_sequences + .iter() + .copied() + .map(Sequence::from); + + let channel_end_path = ChannelEndPath::new(&port_id, &channel_id); + + let acknowledgements = ibc_ctx + .packet_acknowledgements(&channel_end_path, commitment_sequences)? + .into_iter() + .map(|path| { + ibc_ctx + .get_packet_acknowledgement(&path) + .map(|acknowledgement| PacketState { + port_id: path.port_id.as_str().into(), + channel_id: path.channel_id.as_str().into(), + sequence: path.sequence.into(), + data: acknowledgement.into_vec(), + }) + }) + .collect::>()?; + + Ok(QueryPacketAcknowledgementsResponse { + acknowledgements, + height: Some(ibc_ctx.host_height()?.into()), + // no support for pagination yet + pagination: None, + }) +} + +/// Queries for all unreceived packets associated with a channel +pub fn query_unreceived_packets( + ibc_ctx: &I, + request: &QueryUnreceivedPacketsRequest, +) -> Result +where + I: QueryContext, +{ + let channel_id = ChannelId::from_str(request.channel_id.as_str())?; + + let port_id = PortId::from_str(request.port_id.as_str())?; + + let sequences = request + .packet_commitment_sequences + .iter() + .copied() + .map(Sequence::from); + + let channel_end_path = ChannelEndPath::new(&port_id, &channel_id); + + let unreceived_packets = ibc_ctx.unreceived_packets(&channel_end_path, sequences)?; + + Ok(QueryUnreceivedPacketsResponse { + sequences: unreceived_packets.into_iter().map(Into::into).collect(), + height: Some(ibc_ctx.host_height()?.into()), + }) +} + +/// Queries for all unreceived acknowledgements associated with a channel +pub fn query_unreceived_acks( + ibc_ctx: &I, + request: &QueryUnreceivedAcksRequest, +) -> Result +where + I: QueryContext, +{ + let channel_id = ChannelId::from_str(request.channel_id.as_str())?; + + let port_id = PortId::from_str(request.port_id.as_str())?; + + let sequences = request + .packet_ack_sequences + .iter() + .copied() + .map(Sequence::from); + + let channel_end_path = ChannelEndPath::new(&port_id, &channel_id); + + let unreceived_acks = ibc_ctx.unreceived_acks(&channel_end_path, sequences)?; + + Ok(QueryUnreceivedAcksResponse { + sequences: unreceived_acks.into_iter().map(Into::into).collect(), + height: Some(ibc_ctx.host_height()?.into()), + }) +} + +/// Queries for the next sequence send associated with a channel +pub fn query_next_sequence_send( + ibc_ctx: &I, + request: &QueryNextSequenceSendRequest, +) -> Result +where + I: ValidationContext + ProvableContext, +{ + let channel_id = ChannelId::from_str(request.channel_id.as_str())?; + + let port_id = PortId::from_str(request.port_id.as_str())?; + + let next_seq_send_path = SeqSendPath::new(&port_id, &channel_id); + + let next_sequence_send = ibc_ctx.get_next_sequence_send(&next_seq_send_path)?; + + let current_height = ibc_ctx.host_height()?; + + let proof = ibc_ctx + .get_proof(current_height, &Path::SeqSend(next_seq_send_path)) + .ok_or(QueryError::ProofNotFound { + description: format!( + "Next sequence send proof not found for channel {}", + channel_id + ), + })?; + + Ok(QueryNextSequenceSendResponse { + next_sequence_send: next_sequence_send.into(), + proof, + proof_height: Some(current_height.into()), + }) +} + +/// Queries for the next sequence receive associated with a channel +pub fn query_next_sequence_receive( + ibc_ctx: &I, + request: &QueryNextSequenceReceiveRequest, +) -> Result +where + I: ValidationContext + ProvableContext, +{ + let channel_id = ChannelId::from_str(request.channel_id.as_str())?; + + let port_id = PortId::from_str(request.port_id.as_str())?; + + let next_seq_recv_path = SeqRecvPath::new(&port_id, &channel_id); + + let next_sequence_recv = ibc_ctx.get_next_sequence_recv(&next_seq_recv_path)?; + + let current_height = ibc_ctx.host_height()?; + + let proof = ibc_ctx + .get_proof(current_height, &Path::SeqRecv(next_seq_recv_path)) + .ok_or(QueryError::ProofNotFound { + description: format!( + "Next sequence receive proof not found for channel {}", + channel_id + ), + })?; + + Ok(QueryNextSequenceReceiveResponse { + next_sequence_receive: next_sequence_recv.into(), + proof, + proof_height: Some(current_height.into()), + }) +} diff --git a/crates/ibc-query/src/core/channel/service.rs b/crates/ibc-query/src/core/channel/service.rs new file mode 100644 index 000000000..a4f3d2fce --- /dev/null +++ b/crates/ibc-query/src/core/channel/service.rs @@ -0,0 +1,196 @@ +//! [`ChannelQueryService`](ChannelQueryService) takes a generic `I` to store `ibc_context` that implements [`QueryContext`](QueryContext). +//! `I` must be a type where writes from one thread are readable from another. +//! This means using `Arc>` or `Arc>` in most cases. + +use std::boxed::Box; + +use ibc::core::ValidationContext; +use ibc::prelude::*; +use ibc_proto::google::protobuf::Any; +use ibc_proto::ibc::core::channel::v1::query_server::Query as ChannelQuery; +use ibc_proto::ibc::core::channel::v1::{ + QueryChannelClientStateRequest, QueryChannelClientStateResponse, + QueryChannelConsensusStateRequest, QueryChannelConsensusStateResponse, QueryChannelRequest, + QueryChannelResponse, QueryChannelsRequest, QueryChannelsResponse, + QueryConnectionChannelsRequest, QueryConnectionChannelsResponse, + QueryNextSequenceReceiveRequest, QueryNextSequenceReceiveResponse, + QueryNextSequenceSendRequest, QueryNextSequenceSendResponse, QueryPacketAcknowledgementRequest, + QueryPacketAcknowledgementResponse, QueryPacketAcknowledgementsRequest, + QueryPacketAcknowledgementsResponse, QueryPacketCommitmentRequest, + QueryPacketCommitmentResponse, QueryPacketCommitmentsRequest, QueryPacketCommitmentsResponse, + QueryPacketReceiptRequest, QueryPacketReceiptResponse, QueryUnreceivedAcksRequest, + QueryUnreceivedAcksResponse, QueryUnreceivedPacketsRequest, QueryUnreceivedPacketsResponse, +}; +use tonic::{Request, Response, Status}; + +use super::{ + query_channel, query_channel_client_state, query_channel_consensus_state, query_channels, + query_connection_channels, query_next_sequence_receive, query_next_sequence_send, + query_packet_acknowledgement, query_packet_acknowledgements, query_packet_commitment, + query_packet_commitments, query_packet_receipt, query_unreceived_acks, + query_unreceived_packets, +}; +use crate::core::context::QueryContext; + +// TODO(rano): currently the services don't support pagination, so we return all the results. + +/// The generic `I` must be a type where writes from one thread are readable from another. +/// This means using `Arc>` or `Arc>` in most cases. +pub struct ChannelQueryService +where + I: QueryContext + Send + Sync + 'static, + ::AnyClientState: Into, + ::AnyConsensusState: Into, +{ + ibc_context: I, +} + +impl ChannelQueryService +where + I: QueryContext + Send + Sync + 'static, + ::AnyClientState: Into, + ::AnyConsensusState: Into, +{ + /// The parameter `ibc_context` must be a type where writes from one thread are readable from another. + /// This means using `Arc>` or `Arc>` in most cases. + pub fn new(ibc_context: I) -> Self { + Self { ibc_context } + } +} + +#[tonic::async_trait] +impl ChannelQuery for ChannelQueryService +where + I: QueryContext + Send + Sync + 'static, + ::AnyClientState: Into, + ::AnyConsensusState: Into, +{ + async fn channel( + &self, + request: Request, + ) -> Result, Status> { + let response = query_channel(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn channels( + &self, + request: Request, + ) -> Result, Status> { + let response = query_channels(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn connection_channels( + &self, + request: Request, + ) -> Result, Status> { + let response = query_connection_channels(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn channel_client_state( + &self, + request: Request, + ) -> Result, Status> { + let response = query_channel_client_state(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn channel_consensus_state( + &self, + request: Request, + ) -> Result, Status> { + let response = query_channel_consensus_state(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn packet_commitment( + &self, + request: Request, + ) -> Result, Status> { + let response = query_packet_commitment(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn packet_commitments( + &self, + request: Request, + ) -> Result, Status> { + let response = query_packet_commitments(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn packet_receipt( + &self, + request: Request, + ) -> Result, Status> { + let response = query_packet_receipt(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn packet_acknowledgement( + &self, + request: Request, + ) -> Result, Status> { + let response = query_packet_acknowledgement(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + /// Returns all the acknowledgements if sequences is omitted. + async fn packet_acknowledgements( + &self, + request: Request, + ) -> Result, Status> { + let response = query_packet_acknowledgements(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + /// Returns all the unreceived packets if sequences is omitted. + async fn unreceived_packets( + &self, + request: Request, + ) -> Result, Status> { + let response = query_unreceived_packets(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + /// Returns all the unreceived acknowledgements if sequences is omitted. + async fn unreceived_acks( + &self, + request: Request, + ) -> Result, Status> { + let response = query_unreceived_acks(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn next_sequence_send( + &self, + request: Request, + ) -> Result, Status> { + let response = query_next_sequence_send(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn next_sequence_receive( + &self, + request: Request, + ) -> Result, Status> { + let response = query_next_sequence_receive(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } +} diff --git a/crates/ibc-query/src/core/client/mod.rs b/crates/ibc-query/src/core/client/mod.rs new file mode 100644 index 000000000..b24e36180 --- /dev/null +++ b/crates/ibc-query/src/core/client/mod.rs @@ -0,0 +1,5 @@ +mod query; +mod service; + +pub use query::*; +pub use service::*; diff --git a/crates/ibc-query/src/core/client/query.rs b/crates/ibc-query/src/core/client/query.rs new file mode 100644 index 000000000..0f0923eba --- /dev/null +++ b/crates/ibc-query/src/core/client/query.rs @@ -0,0 +1,240 @@ +//! Provides utility functions for querying IBC client states. + +use alloc::format; +use core::str::FromStr; + +use ibc::core::ics02_client::client_state::ClientStateValidation; +use ibc::core::ics02_client::error::ClientError; +use ibc::core::ics24_host::identifier::ClientId; +use ibc::core::ics24_host::path::{ + ClientConsensusStatePath, ClientStatePath, Path, UpgradeClientPath, +}; +use ibc::core::ValidationContext; +use ibc::hosts::tendermint::upgrade_proposal::UpgradeValidationContext; +use ibc::Height; +use ibc_proto::google::protobuf::Any; +use ibc_proto::ibc::core::client::v1::{ + ConsensusStateWithHeight, IdentifiedClientState, QueryClientStateRequest, + QueryClientStateResponse, QueryClientStatesRequest, QueryClientStatesResponse, + QueryClientStatusRequest, QueryClientStatusResponse, QueryConsensusStateHeightsRequest, + QueryConsensusStateHeightsResponse, QueryConsensusStateRequest, QueryConsensusStateResponse, + QueryConsensusStatesRequest, QueryConsensusStatesResponse, QueryUpgradedClientStateRequest, + QueryUpgradedClientStateResponse, QueryUpgradedConsensusStateRequest, + QueryUpgradedConsensusStateResponse, +}; + +use crate::core::context::{ProvableContext, QueryContext}; +use crate::error::QueryError; + +/// Queries for the client state of a given client id. +pub fn query_client_state( + ibc_ctx: &I, + request: &QueryClientStateRequest, +) -> Result +where + I: ValidationContext + ProvableContext, + ::AnyClientState: Into, +{ + let client_id = ClientId::from_str(request.client_id.as_str())?; + + let client_state = ibc_ctx.client_state(&client_id)?; + + let current_height = ibc_ctx.host_height()?; + + let proof = ibc_ctx + .get_proof( + current_height, + &Path::ClientState(ClientStatePath::new(&client_id)), + ) + .ok_or(QueryError::ProofNotFound { + description: format!("Proof not found for client state path: {client_id:?}"), + })?; + + Ok(QueryClientStateResponse { + client_state: Some(client_state.into()), + proof, + proof_height: Some(current_height.into()), + }) +} + +/// Queries for all the existing client states. +pub fn query_client_states( + ibc_ctx: &I, + _request: &QueryClientStatesRequest, +) -> Result +where + I: QueryContext, + ::AnyClientState: Into, +{ + let client_states = ibc_ctx.client_states()?; + + Ok(QueryClientStatesResponse { + client_states: client_states + .into_iter() + .map(|(id, state)| IdentifiedClientState { + client_id: id.into(), + client_state: Some(state.into()), + }) + .collect(), + // no support for pagination yet + pagination: None, + }) +} + +/// Queries for the consensus state of a given client id and height. +pub fn query_consensus_state( + ibc_ctx: &I, + request: &QueryConsensusStateRequest, +) -> Result +where + I: QueryContext, + ::AnyConsensusState: Into, +{ + let client_id = ClientId::from_str(request.client_id.as_str())?; + + let (height, consensus_state) = if request.latest_height { + ibc_ctx + .consensus_states(&client_id)? + .into_iter() + .max_by_key(|(h, _)| *h) + .ok_or(QueryError::ProofNotFound { + description: format!("No consensus state found for client: {client_id:?}"), + })? + } else { + let height = Height::new(request.revision_number, request.revision_height)?; + + let consensus_state = + ibc_ctx.consensus_state(&ClientConsensusStatePath::new(&client_id, &height))?; + + (height, consensus_state) + }; + + let current_height = ibc_ctx.host_height()?; + + let proof = ibc_ctx + .get_proof( + current_height, + &Path::ClientConsensusState(ClientConsensusStatePath::new(&client_id, &height)), + ) + .ok_or(QueryError::ProofNotFound { + description: format!("Proof not found for consensus state path: {client_id:?}"), + })?; + + Ok(QueryConsensusStateResponse { + consensus_state: Some(consensus_state.into()), + proof, + proof_height: Some(current_height.into()), + }) +} + +/// Queries for all the consensus states of a given client id. +pub fn query_consensus_states( + ibc_ctx: &I, + request: &QueryConsensusStatesRequest, +) -> Result +where + I: QueryContext, + ::AnyConsensusState: Into, +{ + let client_id = ClientId::from_str(request.client_id.as_str())?; + + let consensus_states = ibc_ctx.consensus_states(&client_id)?; + + Ok(QueryConsensusStatesResponse { + consensus_states: consensus_states + .into_iter() + .map(|(height, state)| ConsensusStateWithHeight { + height: Some(height.into()), + consensus_state: Some(state.into()), + }) + .collect(), + // no support for pagination yet + pagination: None, + }) +} + +/// Queries for the heights of all the consensus states of a given client id. +pub fn query_consensus_state_heights( + ibc_ctx: &I, + request: &QueryConsensusStateHeightsRequest, +) -> Result +where + I: QueryContext, +{ + let client_id = ClientId::from_str(request.client_id.as_str())?; + + let consensus_state_heights = ibc_ctx.consensus_state_heights(&client_id)?; + + Ok(QueryConsensusStateHeightsResponse { + consensus_state_heights: consensus_state_heights + .into_iter() + .map(|height| height.into()) + .collect(), + // no support for pagination yet + pagination: None, + }) +} + +/// Queries for the status (Active, Frozen, Expired, Unauthorized) of a given client. +pub fn query_client_status( + ibc_ctx: &I, + request: &QueryClientStatusRequest, +) -> Result +where + I: ValidationContext, +{ + let client_id = ClientId::from_str(request.client_id.as_str())?; + + let client_state = ibc_ctx.client_state(&client_id)?; + let client_validation_ctx = ibc_ctx.get_client_validation_context(); + let client_status = client_state.status(client_validation_ctx, &client_id)?; + + Ok(QueryClientStatusResponse { + status: format!("{client_status}"), + }) +} + +/// Queries for the upgraded client state. +pub fn query_upgraded_client_state( + upgrade_ctx: &U, + _request: &QueryUpgradedClientStateRequest, +) -> Result +where + U: UpgradeValidationContext, + ::AnyClientState: Into, +{ + let plan = upgrade_ctx.upgrade_plan().map_err(ClientError::from)?; + + let upgraded_client_state_path = UpgradeClientPath::UpgradedClientState(plan.height); + + let upgraded_client_state = upgrade_ctx + .upgraded_client_state(&upgraded_client_state_path) + .map_err(ClientError::from)?; + + Ok(QueryUpgradedClientStateResponse { + upgraded_client_state: Some(upgraded_client_state.into()), + }) +} + +/// Queries for the upgraded consensus state. +pub fn query_upgraded_consensus_state( + upgrade_ctx: &U, + _request: &QueryUpgradedConsensusStateRequest, +) -> Result +where + U: UpgradeValidationContext, + ::AnyConsensusState: Into, +{ + let plan = upgrade_ctx.upgrade_plan().map_err(ClientError::from)?; + + let upgraded_consensus_state_path = + UpgradeClientPath::UpgradedClientConsensusState(plan.height); + + let upgraded_consensus_state = upgrade_ctx + .upgraded_consensus_state(&upgraded_consensus_state_path) + .map_err(ClientError::from)?; + + Ok(QueryUpgradedConsensusStateResponse { + upgraded_consensus_state: Some(upgraded_consensus_state.into()), + }) +} diff --git a/crates/ibc-query/src/core/client/service.rs b/crates/ibc-query/src/core/client/service.rs new file mode 100644 index 000000000..d6612cf2e --- /dev/null +++ b/crates/ibc-query/src/core/client/service.rs @@ -0,0 +1,156 @@ +//! [`ClientQueryService`](ClientQueryService) takes generics `I` and `U` to store `ibc_context` and `upgrade_context` that implement [`QueryContext`](QueryContext) and [`UpgradeValidationContext`](UpgradeValidationContext) respectively. +//! `I` must be a type where writes from one thread are readable from another. +//! This means using `Arc>` or `Arc>` in most cases. + +use std::boxed::Box; + +use ibc::core::ValidationContext; +use ibc::hosts::tendermint::upgrade_proposal::UpgradeValidationContext; +use ibc::prelude::*; +use ibc_proto::google::protobuf::Any; +use ibc_proto::ibc::core::client::v1::query_server::Query as ClientQuery; +use ibc_proto::ibc::core::client::v1::{ + QueryClientParamsRequest, QueryClientParamsResponse, QueryClientStateRequest, + QueryClientStateResponse, QueryClientStatesRequest, QueryClientStatesResponse, + QueryClientStatusRequest, QueryClientStatusResponse, QueryConsensusStateHeightsRequest, + QueryConsensusStateHeightsResponse, QueryConsensusStateRequest, QueryConsensusStateResponse, + QueryConsensusStatesRequest, QueryConsensusStatesResponse, QueryUpgradedClientStateRequest, + QueryUpgradedClientStateResponse, QueryUpgradedConsensusStateRequest, + QueryUpgradedConsensusStateResponse, +}; +use tonic::{Request, Response, Status}; + +use super::{ + query_client_state, query_client_states, query_client_status, query_consensus_state, + query_consensus_state_heights, query_consensus_states, query_upgraded_client_state, + query_upgraded_consensus_state, +}; +use crate::core::context::QueryContext; + +// TODO(rano): currently the services don't support pagination, so we return all the results. + +/// Generics `I` and `U` must be a type where writes from one thread are readable from another. +/// This means using `Arc>` or `Arc>` in most cases. +pub struct ClientQueryService +where + I: QueryContext + Send + Sync + 'static, + U: UpgradeValidationContext + Send + Sync + 'static, + ::AnyClientState: Into, + ::AnyConsensusState: Into, + ::AnyClientState: Into, + ::AnyConsensusState: Into, +{ + ibc_context: I, + upgrade_context: U, +} + +impl ClientQueryService +where + I: QueryContext + Send + Sync + 'static, + U: UpgradeValidationContext + Send + Sync + 'static, + ::AnyClientState: Into, + ::AnyConsensusState: Into, + ::AnyClientState: Into, + ::AnyConsensusState: Into, +{ + /// Parameters `ibc_context` and `upgrade_context` must be a type where writes from one thread are readable from another. + /// This means using `Arc>` or `Arc>` in most cases. + pub fn new(ibc_context: I, upgrade_context: U) -> Self { + Self { + ibc_context, + upgrade_context, + } + } +} + +#[tonic::async_trait] +impl ClientQuery for ClientQueryService +where + I: QueryContext + Send + Sync + 'static, + U: UpgradeValidationContext + Send + Sync + 'static, + ::AnyClientState: Into, + ::AnyConsensusState: Into, + ::AnyClientState: Into, + ::AnyConsensusState: Into, +{ + async fn client_state( + &self, + request: Request, + ) -> Result, Status> { + let response = query_client_state(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn client_states( + &self, + request: Request, + ) -> Result, Status> { + let response = query_client_states(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn consensus_state( + &self, + request: Request, + ) -> Result, Status> { + let response = query_consensus_state(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn consensus_states( + &self, + request: Request, + ) -> Result, Status> { + let response = query_consensus_states(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn consensus_state_heights( + &self, + request: Request, + ) -> Result, Status> { + let response = query_consensus_state_heights(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn client_status( + &self, + request: Request, + ) -> Result, Status> { + let response = query_client_status(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn client_params( + &self, + _request: Request, + ) -> Result, Status> { + Err(Status::unimplemented( + "Querying ClientParams is not supported yet", + )) + } + + async fn upgraded_client_state( + &self, + request: Request, + ) -> Result, Status> { + let response = query_upgraded_client_state(&self.upgrade_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn upgraded_consensus_state( + &self, + request: Request, + ) -> Result, Status> { + let response = query_upgraded_consensus_state(&self.upgrade_context, request.get_ref())?; + + Ok(Response::new(response)) + } +} diff --git a/crates/ibc-query/src/core/connection/mod.rs b/crates/ibc-query/src/core/connection/mod.rs new file mode 100644 index 000000000..b24e36180 --- /dev/null +++ b/crates/ibc-query/src/core/connection/mod.rs @@ -0,0 +1,5 @@ +mod query; +mod service; + +pub use query::*; +pub use service::*; diff --git a/crates/ibc-query/src/core/connection/query.rs b/crates/ibc-query/src/core/connection/query.rs new file mode 100644 index 000000000..4f712b1eb --- /dev/null +++ b/crates/ibc-query/src/core/connection/query.rs @@ -0,0 +1,195 @@ +//! Provides utility functions for querying IBC connection states. + +use alloc::format; +use alloc::vec::Vec; +use core::str::FromStr; + +use ibc::core::ics24_host::identifier::{ClientId, ConnectionId}; +use ibc::core::ics24_host::path::{ + ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, ConnectionPath, Path, +}; +use ibc::core::ValidationContext; +use ibc::Height; +use ibc_proto::google::protobuf::Any; +use ibc_proto::ibc::core::client::v1::IdentifiedClientState; +use ibc_proto::ibc::core::connection::v1::{ + Params as ConnectionParams, QueryClientConnectionsRequest, QueryClientConnectionsResponse, + QueryConnectionClientStateRequest, QueryConnectionClientStateResponse, + QueryConnectionConsensusStateRequest, QueryConnectionConsensusStateResponse, + QueryConnectionParamsRequest, QueryConnectionParamsResponse, QueryConnectionRequest, + QueryConnectionResponse, QueryConnectionsRequest, QueryConnectionsResponse, +}; + +use crate::core::context::{ProvableContext, QueryContext}; +use crate::error::QueryError; + +/// Queries for the connection end of a given connection id. +pub fn query_connection( + ibc_ctx: &I, + request: &QueryConnectionRequest, +) -> Result +where + I: ValidationContext + ProvableContext, +{ + let connection_id = ConnectionId::from_str(request.connection_id.as_str())?; + + let connection_end = ibc_ctx.connection_end(&connection_id)?; + + let current_height = ibc_ctx.host_height()?; + + let proof = ibc_ctx + .get_proof( + current_height, + &Path::Connection(ConnectionPath::new(&connection_id)), + ) + .ok_or(QueryError::ProofNotFound { + description: format!("Proof not found for connection path: {connection_id:?}"), + })?; + + Ok(QueryConnectionResponse { + connection: Some(connection_end.into()), + proof, + proof_height: Some(current_height.into()), + }) +} + +/// Queries for all the existing connection ends. +pub fn query_connections( + ibc_ctx: &I, + _request: &QueryConnectionsRequest, +) -> Result +where + I: QueryContext, +{ + let connections = ibc_ctx.connection_ends()?; + + Ok(QueryConnectionsResponse { + connections: connections.into_iter().map(Into::into).collect(), + height: Some(ibc_ctx.host_height()?.into()), + // no support for pagination yet + pagination: None, + }) +} + +/// Queries for all the existing connection ends for a given client. +pub fn query_client_connections( + ibc_ctx: &I, + request: &QueryClientConnectionsRequest, +) -> Result +where + I: QueryContext, + ::AnyClientState: Into, +{ + let client_id = ClientId::from_str(request.client_id.as_str())?; + + let connections = ibc_ctx.client_connection_ends(&client_id)?; + + let current_height = ibc_ctx.host_height()?; + + let proof: Vec = ibc_ctx + .get_proof( + current_height, + &Path::ClientConnection(ClientConnectionPath::new(&client_id)), + ) + .ok_or(QueryError::ProofNotFound { + description: format!("Proof not found for client connection path: {client_id:?}"), + })?; + + Ok(QueryClientConnectionsResponse { + connection_paths: connections.into_iter().map(|x| x.as_str().into()).collect(), + proof, + proof_height: Some(current_height.into()), + }) +} + +/// Queries for the client state of a given connection id. +pub fn query_connection_client_state( + ibc_ctx: &I, + request: &QueryConnectionClientStateRequest, +) -> Result +where + I: QueryContext, + ::AnyClientState: Into, +{ + let connection_id = ConnectionId::from_str(request.connection_id.as_str())?; + + let connection_end = ibc_ctx.connection_end(&connection_id)?; + + let client_state = ibc_ctx.client_state(connection_end.client_id())?; + + let current_height = ibc_ctx.host_height()?; + + let proof = ibc_ctx + .get_proof( + current_height, + &Path::ClientState(ClientStatePath::new(connection_end.client_id())), + ) + .ok_or(QueryError::ProofNotFound { + description: format!( + "Proof not found for client state path: {:?}", + connection_end.client_id() + ), + })?; + + Ok(QueryConnectionClientStateResponse { + identified_client_state: Some(IdentifiedClientState { + client_id: connection_end.client_id().as_str().into(), + client_state: Some(client_state.into()), + }), + proof, + proof_height: Some(current_height.into()), + }) +} + +/// Queries for the consensus state of a given connection id and height. +pub fn query_connection_consensus_state( + ibc_ctx: &I, + request: &QueryConnectionConsensusStateRequest, +) -> Result +where + I: ValidationContext + ProvableContext, + ::AnyConsensusState: Into, +{ + let connection_id = ConnectionId::from_str(request.connection_id.as_str())?; + + let connection_end = ibc_ctx.connection_end(&connection_id)?; + + let height = Height::new(request.revision_number, request.revision_height)?; + + let consensus_path = ClientConsensusStatePath::new(connection_end.client_id(), &height); + + let consensus_state = ibc_ctx.consensus_state(&consensus_path)?; + + let current_height = ibc_ctx.host_height()?; + + let proof = ibc_ctx + .get_proof(current_height, &Path::ClientConsensusState(consensus_path)) + .ok_or(QueryError::ProofNotFound { + description: format!( + "Proof not found for consensus state path: {:?}", + connection_end.client_id() + ), + })?; + + Ok(QueryConnectionConsensusStateResponse { + consensus_state: Some(consensus_state.into()), + client_id: connection_end.client_id().as_str().into(), + proof, + proof_height: Some(current_height.into()), + }) +} + +/// Queries for the connection parameters. +pub fn query_connection_params( + ibc_ctx: &I, + _request: &QueryConnectionParamsRequest, +) -> Result +where + I: QueryContext, +{ + Ok(QueryConnectionParamsResponse { + params: Some(ConnectionParams { + max_expected_time_per_block: ibc_ctx.max_expected_time_per_block().as_secs(), + }), + }) +} diff --git a/crates/ibc-query/src/core/connection/service.rs b/crates/ibc-query/src/core/connection/service.rs new file mode 100644 index 000000000..a14384e34 --- /dev/null +++ b/crates/ibc-query/src/core/connection/service.rs @@ -0,0 +1,112 @@ +//! [`ConnectionQueryService`](ConnectionQueryService) takes a generic `I` to store `ibc_context` that implements [`QueryContext`](QueryContext). +//! `I` must be a type where writes from one thread are readable from another. +//! This means using `Arc>` or `Arc>` in most cases. + +use std::boxed::Box; + +use ibc::core::ValidationContext; +use ibc::prelude::*; +use ibc_proto::google::protobuf::Any; +use ibc_proto::ibc::core::connection::v1::query_server::Query as ConnectionQuery; +use ibc_proto::ibc::core::connection::v1::{ + QueryClientConnectionsRequest, QueryClientConnectionsResponse, + QueryConnectionClientStateRequest, QueryConnectionClientStateResponse, + QueryConnectionConsensusStateRequest, QueryConnectionConsensusStateResponse, + QueryConnectionParamsRequest, QueryConnectionParamsResponse, QueryConnectionRequest, + QueryConnectionResponse, QueryConnectionsRequest, QueryConnectionsResponse, +}; +use tonic::{Request, Response, Status}; + +use super::{ + query_client_connections, query_connection, query_connection_client_state, + query_connection_consensus_state, query_connection_params, query_connections, +}; +use crate::core::context::QueryContext; + +// TODO(rano): currently the services don't support pagination, so we return all the results. + +/// The generic `I` must be a type where writes from one thread are readable from another. +/// This means using `Arc>` or `Arc>` in most cases. +pub struct ConnectionQueryService +where + I: QueryContext + Send + Sync + 'static, + ::AnyClientState: Into, + ::AnyConsensusState: Into, +{ + ibc_context: I, +} + +impl ConnectionQueryService +where + I: QueryContext + Send + Sync + 'static, + ::AnyClientState: Into, + ::AnyConsensusState: Into, +{ + /// The parameter `ibc_context` must be a type where writes from one thread are readable from another. + /// This means using `Arc>` or `Arc>` in most cases. + pub fn new(ibc_context: I) -> Self { + Self { ibc_context } + } +} + +#[tonic::async_trait] +impl ConnectionQuery for ConnectionQueryService +where + I: QueryContext + Send + Sync + 'static, + ::AnyClientState: Into, + ::AnyConsensusState: Into, +{ + async fn connection( + &self, + request: Request, + ) -> Result, Status> { + let response = query_connection(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn connections( + &self, + request: Request, + ) -> Result, Status> { + let response = query_connections(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn client_connections( + &self, + request: Request, + ) -> Result, Status> { + let response = query_client_connections(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn connection_client_state( + &self, + request: Request, + ) -> Result, Status> { + let response = query_connection_client_state(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn connection_consensus_state( + &self, + request: Request, + ) -> Result, Status> { + let response = query_connection_consensus_state(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } + + async fn connection_params( + &self, + request: Request, + ) -> Result, Status> { + let response = query_connection_params(&self.ibc_context, request.get_ref())?; + + Ok(Response::new(response)) + } +} diff --git a/crates/ibc/src/services/core/context.rs b/crates/ibc-query/src/core/context.rs similarity index 87% rename from crates/ibc/src/services/core/context.rs rename to crates/ibc-query/src/core/context.rs index 14632f25f..0316f7090 100644 --- a/crates/ibc/src/services/core/context.rs +++ b/crates/ibc-query/src/core/context.rs @@ -1,13 +1,13 @@ //! Required traits for blanket implementations of [`gRPC query services`](crate::services::core). -use crate::core::ics03_connection::connection::IdentifiedConnectionEnd; -use crate::core::ics04_channel::channel::IdentifiedChannelEnd; -use crate::core::ics04_channel::packet::Sequence; -use crate::core::ics24_host::identifier::{ClientId, ConnectionId}; -use crate::core::ics24_host::path::{AckPath, ChannelEndPath, CommitmentPath, Path}; -use crate::core::{ContextError, ValidationContext}; -use crate::prelude::*; -use crate::Height; +use ibc::core::ics03_connection::connection::IdentifiedConnectionEnd; +use ibc::core::ics04_channel::channel::IdentifiedChannelEnd; +use ibc::core::ics04_channel::packet::Sequence; +use ibc::core::ics24_host::identifier::{ClientId, ConnectionId}; +use ibc::core::ics24_host::path::{AckPath, ChannelEndPath, CommitmentPath, Path}; +use ibc::core::{ContextError, ValidationContext}; +use ibc::prelude::*; +use ibc::Height; /// Context to be implemented by the host to provide proofs in gRPC query responses /// diff --git a/crates/ibc/src/services/core/mod.rs b/crates/ibc-query/src/core/mod.rs similarity index 100% rename from crates/ibc/src/services/core/mod.rs rename to crates/ibc-query/src/core/mod.rs diff --git a/crates/ibc-query/src/error.rs b/crates/ibc-query/src/error.rs new file mode 100644 index 000000000..47670d293 --- /dev/null +++ b/crates/ibc-query/src/error.rs @@ -0,0 +1,46 @@ +use displaydoc::Display; +use ibc::core::ics02_client::error::ClientError; +use ibc::core::ics24_host::identifier::IdentifierError; +use ibc::core::ContextError; +use tonic::Status; + +#[derive(Debug, Display)] +pub enum QueryError { + /// Context error: {0} + ContextError(ContextError), + /// Client error: {0} + ClientError(ClientError), + /// Identifier error: {0} + IdentifierError(IdentifierError), + /// Proof not found: {description} + ProofNotFound { description: String }, +} + +impl From for Status { + fn from(e: QueryError) -> Self { + match e { + QueryError::ContextError(e) => Status::internal(e.to_string()), + QueryError::ClientError(e) => Status::internal(e.to_string()), + QueryError::IdentifierError(e) => Status::internal(e.to_string()), + QueryError::ProofNotFound { description } => Status::not_found(description), + } + } +} + +impl From for QueryError { + fn from(e: ContextError) -> Self { + QueryError::ContextError(e) + } +} + +impl From for QueryError { + fn from(e: ClientError) -> Self { + QueryError::ClientError(e) + } +} + +impl From for QueryError { + fn from(e: IdentifierError) -> Self { + QueryError::IdentifierError(e) + } +} diff --git a/crates/ibc/src/services/mod.rs b/crates/ibc-query/src/lib.rs similarity index 95% rename from crates/ibc/src/services/mod.rs rename to crates/ibc-query/src/lib.rs index d90ad3055..bccac04d8 100644 --- a/crates/ibc/src/services/mod.rs +++ b/crates/ibc-query/src/lib.rs @@ -1,4 +1,4 @@ -//! Implementation of the gRPC services of core IBC components. +//! Implementation of the gRPC query services of core IBC components. //! //! The provided structs includes blanket implementation of their corresponding gRPC service traits, //! if the host implements the following _context_ traits. @@ -44,5 +44,7 @@ //! .serve(addr); //! ``` +extern crate alloc; + pub mod core; pub mod error; diff --git a/crates/ibc/Cargo.toml b/crates/ibc/Cargo.toml index 8e747ab98..0f8a9b063 100644 --- a/crates/ibc/Cargo.toml +++ b/crates/ibc/Cargo.toml @@ -37,8 +37,6 @@ std = [ ] parity-scale-codec = ["dep:parity-scale-codec", "dep:scale-info", "ibc-proto/parity-scale-codec"] borsh = ["dep:borsh", "ibc-proto/borsh"] -# includes gRPC services for IBC core -grpc = ["dep:tonic", "ibc-proto/server"] # This feature is required for token transfer (ICS-20) serde = ["dep:serde", "dep:serde_derive", "serde_json", "ics23/serde"] @@ -68,7 +66,6 @@ num-traits = { version = "0.2.15", default-features = false } derive_more = { version = "0.99.17", default-features = false, features = ["from", "into", "display", "try_into"] } uint = { version = "0.9", default-features = false } primitive-types = { version = "0.12.0", default-features = false, features = ["serde_no_std"] } -tonic = { version = "0.9", optional = true } ## for codec encode or decode parity-scale-codec = { version = "3.0.0", default-features = false, features = ["full"], optional = true } diff --git a/crates/ibc/src/lib.rs b/crates/ibc/src/lib.rs index 46b9c3a53..7f0866c80 100644 --- a/crates/ibc/src/lib.rs +++ b/crates/ibc/src/lib.rs @@ -54,15 +54,12 @@ pub mod clients; pub mod core; pub mod hosts; -#[cfg(feature = "grpc")] -pub mod services; - #[cfg(any(test, feature = "mocks"))] pub mod mock; #[cfg(any(test, feature = "mocks"))] pub mod test_utils; // Context mock, the underlying host chain, and client types: for testing all handlers. -mod prelude; +pub mod prelude; mod signer; mod utils; diff --git a/crates/ibc/src/services/core/channel.rs b/crates/ibc/src/services/core/channel.rs deleted file mode 100644 index 3442f569d..000000000 --- a/crates/ibc/src/services/core/channel.rs +++ /dev/null @@ -1,559 +0,0 @@ -//! [`ChannelQueryService`](ChannelQueryService) takes a generic `I` to store `ibc_context` that implements [`QueryContext`](QueryContext). -//! `I` must be a type where writes from one thread are readable from another. -//! This means using `Arc>` or `Arc>` in most cases. - -use alloc::str::FromStr; -use std::boxed::Box; - -use ibc_proto::google::protobuf::Any; -use ibc_proto::ibc::core::channel::v1::query_server::Query as ChannelQuery; -use ibc_proto::ibc::core::channel::v1::{ - PacketState, QueryChannelClientStateRequest, QueryChannelClientStateResponse, - QueryChannelConsensusStateRequest, QueryChannelConsensusStateResponse, QueryChannelRequest, - QueryChannelResponse, QueryChannelsRequest, QueryChannelsResponse, - QueryConnectionChannelsRequest, QueryConnectionChannelsResponse, - QueryNextSequenceReceiveRequest, QueryNextSequenceReceiveResponse, - QueryNextSequenceSendRequest, QueryNextSequenceSendResponse, QueryPacketAcknowledgementRequest, - QueryPacketAcknowledgementResponse, QueryPacketAcknowledgementsRequest, - QueryPacketAcknowledgementsResponse, QueryPacketCommitmentRequest, - QueryPacketCommitmentResponse, QueryPacketCommitmentsRequest, QueryPacketCommitmentsResponse, - QueryPacketReceiptRequest, QueryPacketReceiptResponse, QueryUnreceivedAcksRequest, - QueryUnreceivedAcksResponse, QueryUnreceivedPacketsRequest, QueryUnreceivedPacketsResponse, -}; -use ibc_proto::ibc::core::client::v1::IdentifiedClientState; -use tonic::{Request, Response, Status}; - -use crate::core::ics04_channel::packet::Sequence; -use crate::core::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; -use crate::core::ics24_host::path::{ - AckPath, ChannelEndPath, ClientConsensusStatePath, ClientStatePath, CommitmentPath, Path, - ReceiptPath, SeqRecvPath, SeqSendPath, -}; -use crate::core::ValidationContext; -use crate::prelude::*; -use crate::services::core::context::QueryContext; -use crate::Height; - -// TODO(rano): currently the services don't support pagination, so we return all the results. - -/// The generic `I` must be a type where writes from one thread are readable from another. -/// This means using `Arc>` or `Arc>` in most cases. -pub struct ChannelQueryService -where - I: QueryContext + Send + Sync + 'static, - ::AnyClientState: Into, - ::AnyConsensusState: Into, -{ - ibc_context: I, -} - -impl ChannelQueryService -where - I: QueryContext + Send + Sync + 'static, - ::AnyClientState: Into, - ::AnyConsensusState: Into, -{ - /// The parameter `ibc_context` must be a type where writes from one thread are readable from another. - /// This means using `Arc>` or `Arc>` in most cases. - pub fn new(ibc_context: I) -> Self { - Self { ibc_context } - } -} - -#[tonic::async_trait] -impl ChannelQuery for ChannelQueryService -where - I: QueryContext + Send + Sync + 'static, - ::AnyClientState: Into, - ::AnyConsensusState: Into, -{ - async fn channel( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let channel_id = ChannelId::from_str(request_ref.channel_id.as_str())?; - - let port_id = PortId::from_str(request_ref.port_id.as_str())?; - - let channel_end_path = ChannelEndPath::new(&port_id, &channel_id); - - let channel_end = self.ibc_context.channel_end(&channel_end_path)?; - - let current_height = self.ibc_context.host_height()?; - - let proof = self - .ibc_context - .get_proof(current_height, &Path::ChannelEnd(channel_end_path)) - .ok_or_else(|| { - Status::not_found(format!( - "Channel end proof not found for channel {}", - channel_id - )) - })?; - - Ok(Response::new(QueryChannelResponse { - channel: Some(channel_end.into()), - proof, - proof_height: Some(current_height.into()), - })) - } - - async fn channels( - &self, - _request: Request, - ) -> Result, Status> { - let channel_ends = self.ibc_context.channel_ends()?; - - Ok(Response::new(QueryChannelsResponse { - channels: channel_ends.into_iter().map(Into::into).collect(), - height: Some(self.ibc_context.host_height()?.into()), - // no support for pagination yet - pagination: None, - })) - } - - async fn connection_channels( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let connection_id = ConnectionId::from_str(request_ref.connection.as_str())?; - - let all_channel_ends = self.ibc_context.channel_ends()?; - - let connection_channel_ends = all_channel_ends - .into_iter() - .filter(|channel_end| { - channel_end - .channel_end - .connection_hops() - .iter() - .any(|connection_hop| connection_hop == &connection_id) - }) - .map(Into::into) - .collect(); - - Ok(Response::new(QueryConnectionChannelsResponse { - channels: connection_channel_ends, - height: Some(self.ibc_context.host_height()?.into()), - // no support for pagination yet - pagination: None, - })) - } - - async fn channel_client_state( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let channel_id = ChannelId::from_str(request_ref.channel_id.as_str())?; - - let port_id = PortId::from_str(request_ref.port_id.as_str())?; - - let channel_end_path = ChannelEndPath::new(&port_id, &channel_id); - - let channel_end = self.ibc_context.channel_end(&channel_end_path)?; - - let connection_end = channel_end - .connection_hops() - .first() - .map(|connection_id| self.ibc_context.connection_end(connection_id)) - .ok_or_else(|| { - Status::not_found(format!("Channel {} has no connection hops", channel_id)) - })??; - - let client_state = self.ibc_context.client_state(connection_end.client_id())?; - - let current_height = self.ibc_context.host_height()?; - - let proof = self - .ibc_context - .get_proof( - current_height, - &Path::ClientState(ClientStatePath::new(connection_end.client_id())), - ) - .ok_or_else(|| { - Status::not_found(format!( - "Client state proof not found for client {}", - connection_end.client_id() - )) - })?; - - Ok(Response::new(QueryChannelClientStateResponse { - identified_client_state: Some(IdentifiedClientState { - client_id: connection_end.client_id().as_str().into(), - client_state: Some(client_state.into()), - }), - proof, - proof_height: Some(current_height.into()), - })) - } - - async fn channel_consensus_state( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let channel_id = ChannelId::from_str(request_ref.channel_id.as_str())?; - - let port_id = PortId::from_str(request_ref.port_id.as_str())?; - - let height = Height::new(request_ref.revision_number, request_ref.revision_height) - .map_err(|e| Status::invalid_argument(e.to_string()))?; - - let channel_end_path = ChannelEndPath::new(&port_id, &channel_id); - - let channel_end = self.ibc_context.channel_end(&channel_end_path)?; - - let connection_end = channel_end - .connection_hops() - .first() - .map(|connection_id| self.ibc_context.connection_end(connection_id)) - .ok_or_else(|| { - Status::not_found(format!("Channel {} has no connection hops", channel_id)) - })??; - - let consensus_path = ClientConsensusStatePath::new(connection_end.client_id(), &height); - - let consensus_state = self.ibc_context.consensus_state(&consensus_path)?; - - let current_height = self.ibc_context.host_height()?; - - let proof = self - .ibc_context - .get_proof(current_height, &Path::ClientConsensusState(consensus_path)) - .ok_or_else(|| { - Status::not_found(format!( - "Consensus state proof not found for client {}", - connection_end.client_id() - )) - })?; - - Ok(Response::new(QueryChannelConsensusStateResponse { - client_id: connection_end.client_id().as_str().into(), - consensus_state: Some(consensus_state.into()), - proof, - proof_height: Some(current_height.into()), - })) - } - - async fn packet_commitment( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let channel_id = ChannelId::from_str(request_ref.channel_id.as_str())?; - - let port_id = PortId::from_str(request_ref.port_id.as_str())?; - - let sequence = Sequence::from(request_ref.sequence); - - let commitment_path = CommitmentPath::new(&port_id, &channel_id, sequence); - - let packet_commitment_data = self.ibc_context.get_packet_commitment(&commitment_path)?; - - let current_height = self.ibc_context.host_height()?; - - let proof = self - .ibc_context - .get_proof(current_height, &Path::Commitment(commitment_path)) - .ok_or_else(|| { - Status::not_found(format!( - "Packet commitment proof not found for channel {}", - channel_id - )) - })?; - - Ok(Response::new(QueryPacketCommitmentResponse { - commitment: packet_commitment_data.into_vec(), - proof, - proof_height: Some(current_height.into()), - })) - } - - async fn packet_commitments( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let channel_id = ChannelId::from_str(request_ref.channel_id.as_str())?; - - let port_id = PortId::from_str(request_ref.port_id.as_str())?; - - let channel_end_path = ChannelEndPath::new(&port_id, &channel_id); - - let commitments = self - .ibc_context - .packet_commitments(&channel_end_path)? - .into_iter() - .map(|path| { - self.ibc_context - .get_packet_commitment(&path) - .map(|commitment| PacketState { - port_id: path.port_id.as_str().into(), - channel_id: path.channel_id.as_str().into(), - sequence: path.sequence.into(), - data: commitment.into_vec(), - }) - }) - .collect::>()?; - - Ok(Response::new(QueryPacketCommitmentsResponse { - commitments, - height: Some(self.ibc_context.host_height()?.into()), - // no support for pagination yet - pagination: None, - })) - } - - async fn packet_receipt( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let channel_id = ChannelId::from_str(request_ref.channel_id.as_str())?; - - let port_id = PortId::from_str(request_ref.port_id.as_str())?; - - let sequence = Sequence::from(request_ref.sequence); - - let receipt_path = ReceiptPath::new(&port_id, &channel_id, sequence); - - // Receipt only has one enum - // Unreceived packets are not stored - let packet_receipt_data = self.ibc_context.get_packet_receipt(&receipt_path); - - let current_height = self.ibc_context.host_height()?; - - let proof = self - .ibc_context - .get_proof(current_height, &Path::Receipt(receipt_path)) - .ok_or_else(|| { - Status::not_found(format!( - "Packet receipt proof not found for channel {}", - channel_id - )) - })?; - - Ok(Response::new(QueryPacketReceiptResponse { - received: packet_receipt_data.is_ok(), - proof, - proof_height: Some(current_height.into()), - })) - } - - async fn packet_acknowledgement( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let channel_id = ChannelId::from_str(request_ref.channel_id.as_str())?; - - let port_id = PortId::from_str(request_ref.port_id.as_str())?; - - let sequence = Sequence::from(request_ref.sequence); - - let acknowledgement_path = AckPath::new(&port_id, &channel_id, sequence); - - let packet_acknowledgement_data = self - .ibc_context - .get_packet_acknowledgement(&acknowledgement_path)?; - - let current_height = self.ibc_context.host_height()?; - - let proof = self - .ibc_context - .get_proof(current_height, &Path::Ack(acknowledgement_path)) - .ok_or_else(|| { - Status::not_found(format!( - "Packet acknowledgement proof not found for channel {}", - channel_id - )) - })?; - - Ok(Response::new(QueryPacketAcknowledgementResponse { - acknowledgement: packet_acknowledgement_data.into_vec(), - proof, - proof_height: Some(current_height.into()), - })) - } - - /// Returns all the acknowledgements if sequences is omitted. - async fn packet_acknowledgements( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let channel_id = ChannelId::from_str(request_ref.channel_id.as_str())?; - - let port_id = PortId::from_str(request_ref.port_id.as_str())?; - - let commitment_sequences = request_ref - .packet_commitment_sequences - .iter() - .copied() - .map(Sequence::from); - - let channel_end_path = ChannelEndPath::new(&port_id, &channel_id); - - let acknowledgements = self - .ibc_context - .packet_acknowledgements(&channel_end_path, commitment_sequences)? - .into_iter() - .map(|path| { - self.ibc_context - .get_packet_acknowledgement(&path) - .map(|acknowledgement| PacketState { - port_id: path.port_id.as_str().into(), - channel_id: path.channel_id.as_str().into(), - sequence: path.sequence.into(), - data: acknowledgement.into_vec(), - }) - }) - .collect::>()?; - - Ok(Response::new(QueryPacketAcknowledgementsResponse { - acknowledgements, - height: Some(self.ibc_context.host_height()?.into()), - // no support for pagination yet - pagination: None, - })) - } - - async fn unreceived_packets( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let channel_id = ChannelId::from_str(request_ref.channel_id.as_str())?; - - let port_id = PortId::from_str(request_ref.port_id.as_str())?; - - let sequences = request_ref - .packet_commitment_sequences - .iter() - .copied() - .map(Sequence::from); - - let channel_end_path = ChannelEndPath::new(&port_id, &channel_id); - - let unreceived_packets = self - .ibc_context - .unreceived_packets(&channel_end_path, sequences)?; - - Ok(Response::new(QueryUnreceivedPacketsResponse { - sequences: unreceived_packets.into_iter().map(Into::into).collect(), - height: Some(self.ibc_context.host_height()?.into()), - })) - } - - /// Returns all the unreceived acknowledgements if sequences is omitted. - async fn unreceived_acks( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let channel_id = ChannelId::from_str(request_ref.channel_id.as_str())?; - - let port_id = PortId::from_str(request_ref.port_id.as_str())?; - - let sequences = request_ref - .packet_ack_sequences - .iter() - .copied() - .map(Sequence::from); - - let channel_end_path = ChannelEndPath::new(&port_id, &channel_id); - - let unreceived_acks = self - .ibc_context - .unreceived_acks(&channel_end_path, sequences)?; - - Ok(Response::new(QueryUnreceivedAcksResponse { - sequences: unreceived_acks.into_iter().map(Into::into).collect(), - height: Some(self.ibc_context.host_height()?.into()), - })) - } - - async fn next_sequence_receive( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let channel_id = ChannelId::from_str(request_ref.channel_id.as_str())?; - - let port_id = PortId::from_str(request_ref.port_id.as_str())?; - - let next_seq_recv_path = SeqRecvPath::new(&port_id, &channel_id); - - let next_sequence_recv = self - .ibc_context - .get_next_sequence_recv(&next_seq_recv_path)?; - - let current_height = self.ibc_context.host_height()?; - - let proof = self - .ibc_context - .get_proof(current_height, &Path::SeqRecv(next_seq_recv_path)) - .ok_or_else(|| { - Status::not_found(format!( - "Next sequence receive proof not found for channel {}", - channel_id - )) - })?; - - Ok(Response::new(QueryNextSequenceReceiveResponse { - next_sequence_receive: next_sequence_recv.into(), - proof, - proof_height: Some(current_height.into()), - })) - } - - async fn next_sequence_send( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let channel_id = ChannelId::from_str(request_ref.channel_id.as_str())?; - - let port_id = PortId::from_str(request_ref.port_id.as_str())?; - - let next_seq_send_path = SeqSendPath::new(&port_id, &channel_id); - - let next_sequence_send = self - .ibc_context - .get_next_sequence_send(&next_seq_send_path)?; - - let current_height = self.ibc_context.host_height()?; - - let proof = self - .ibc_context - .get_proof(current_height, &Path::SeqSend(next_seq_send_path)) - .ok_or_else(|| { - Status::not_found(format!( - "Next sequence send proof not found for channel {}", - channel_id - )) - })?; - - Ok(Response::new(QueryNextSequenceSendResponse { - next_sequence_send: next_sequence_send.into(), - proof, - proof_height: Some(current_height.into()), - })) - } -} diff --git a/crates/ibc/src/services/core/client.rs b/crates/ibc/src/services/core/client.rs deleted file mode 100644 index 11a7ce548..000000000 --- a/crates/ibc/src/services/core/client.rs +++ /dev/null @@ -1,298 +0,0 @@ -//! [`ClientQueryService`](ClientQueryService) takes generics `I` and `U` to store `ibc_context` and `upgrade_context` that implement [`QueryContext`](QueryContext) and [`UpgradeValidationContext`](UpgradeValidationContext) respectively. -//! `I` must be a type where writes from one thread are readable from another. -//! This means using `Arc>` or `Arc>` in most cases. - -use core::str::FromStr; -use std::boxed::Box; - -use ibc_proto::google::protobuf::Any; -use ibc_proto::ibc::core::client::v1::query_server::Query as ClientQuery; -use ibc_proto::ibc::core::client::v1::{ - ConsensusStateWithHeight, IdentifiedClientState, QueryClientParamsRequest, - QueryClientParamsResponse, QueryClientStateRequest, QueryClientStateResponse, - QueryClientStatesRequest, QueryClientStatesResponse, QueryClientStatusRequest, - QueryClientStatusResponse, QueryConsensusStateHeightsRequest, - QueryConsensusStateHeightsResponse, QueryConsensusStateRequest, QueryConsensusStateResponse, - QueryConsensusStatesRequest, QueryConsensusStatesResponse, QueryUpgradedClientStateRequest, - QueryUpgradedClientStateResponse, QueryUpgradedConsensusStateRequest, - QueryUpgradedConsensusStateResponse, -}; -use tonic::{Request, Response, Status}; - -use crate::core::ics02_client::client_state::ClientStateValidation; -use crate::core::ics02_client::error::ClientError; -use crate::core::ics24_host::identifier::ClientId; -use crate::core::ics24_host::path::{ - ClientConsensusStatePath, ClientStatePath, Path, UpgradeClientPath, -}; -use crate::core::{ContextError, ValidationContext}; -use crate::hosts::tendermint::upgrade_proposal::UpgradeValidationContext; -use crate::prelude::*; -use crate::services::core::context::QueryContext; -use crate::Height; - -// TODO(rano): currently the services don't support pagination, so we return all the results. - -/// Generics `I` and `U` must be a type where writes from one thread are readable from another. -/// This means using `Arc>` or `Arc>` in most cases. -pub struct ClientQueryService -where - I: QueryContext + Send + Sync + 'static, - U: UpgradeValidationContext + Send + Sync + 'static, - ::AnyClientState: Into, - ::AnyConsensusState: Into, - ::AnyClientState: Into, - ::AnyConsensusState: Into, -{ - ibc_context: I, - upgrade_context: U, -} - -impl ClientQueryService -where - I: QueryContext + Send + Sync + 'static, - U: UpgradeValidationContext + Send + Sync + 'static, - ::AnyClientState: Into, - ::AnyConsensusState: Into, - ::AnyClientState: Into, - ::AnyConsensusState: Into, -{ - /// Parameters `ibc_context` and `upgrade_context` must be a type where writes from one thread are readable from another. - /// This means using `Arc>` or `Arc>` in most cases. - pub fn new(ibc_context: I, upgrade_context: U) -> Self { - Self { - ibc_context, - upgrade_context, - } - } -} - -#[tonic::async_trait] -impl ClientQuery for ClientQueryService -where - I: QueryContext + Send + Sync + 'static, - U: UpgradeValidationContext + Send + Sync + 'static, - ::AnyClientState: Into, - ::AnyConsensusState: Into, - ::AnyClientState: Into, - ::AnyConsensusState: Into, -{ - async fn client_state( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let client_id = ClientId::from_str(request_ref.client_id.as_str())?; - let client_state = self.ibc_context.client_state(&client_id)?; - - let current_height = self.ibc_context.host_height()?; - - let proof = self - .ibc_context - .get_proof( - current_height, - &Path::ClientState(ClientStatePath::new(&client_id)), - ) - .ok_or_else(|| { - Status::not_found(format!( - "Proof unavailable for client {} at height {}", - client_id, current_height - )) - })?; - - Ok(Response::new(QueryClientStateResponse { - client_state: Some(client_state.into()), - proof, - proof_height: Some(current_height.into()), - })) - } - - async fn client_states( - &self, - _request: Request, - ) -> Result, Status> { - let client_states = self.ibc_context.client_states()?; - - Ok(Response::new(QueryClientStatesResponse { - client_states: client_states - .into_iter() - .map(|(id, state)| IdentifiedClientState { - client_id: id.into(), - client_state: Some(state.into()), - }) - .collect(), - // no support for pagination yet - pagination: None, - })) - } - - async fn consensus_state( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let client_id = ClientId::from_str(request_ref.client_id.as_str())?; - - let (height, consensus_state) = if request_ref.latest_height { - self.ibc_context - .consensus_states(&client_id)? - .into_iter() - .max_by_key(|(h, _)| *h) - .ok_or_else(|| { - Status::not_found(format!( - "Consensus state not found for client {}", - client_id - )) - })? - } else { - let height = Height::new(request_ref.revision_number, request_ref.revision_height) - .map_err(|e| Status::invalid_argument(e.to_string()))?; - let consensus_state = self - .ibc_context - .consensus_state(&ClientConsensusStatePath::new(&client_id, &height))?; - - (height, consensus_state) - }; - - let current_height = self.ibc_context.host_height()?; - - let proof = self - .ibc_context - .get_proof( - current_height, - &Path::ClientConsensusState(ClientConsensusStatePath::new(&client_id, &height)), - ) - .ok_or_else(|| { - Status::not_found(format!( - "Consensus state not found for client {} at height {}", - client_id, height - )) - })?; - - Ok(Response::new(QueryConsensusStateResponse { - consensus_state: Some(consensus_state.into()), - proof, - proof_height: Some(current_height.into()), - })) - } - - async fn consensus_states( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let client_id = ClientId::from_str(request_ref.client_id.as_str())?; - - let consensus_states = self.ibc_context.consensus_states(&client_id)?; - - Ok(Response::new(QueryConsensusStatesResponse { - consensus_states: consensus_states - .into_iter() - .map(|(height, state)| ConsensusStateWithHeight { - height: Some(height.into()), - consensus_state: Some(state.into()), - }) - .collect(), - // no support for pagination yet - pagination: None, - })) - } - - async fn consensus_state_heights( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let client_id = ClientId::from_str(request_ref.client_id.as_str())?; - - let consensus_state_heights = self.ibc_context.consensus_state_heights(&client_id)?; - - Ok(Response::new(QueryConsensusStateHeightsResponse { - consensus_state_heights: consensus_state_heights - .into_iter() - .map(|height| height.into()) - .collect(), - // no support for pagination yet - pagination: None, - })) - } - - async fn client_status( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let client_id = ClientId::from_str(request_ref.client_id.as_str())?; - - let client_state = self.ibc_context.client_state(&client_id)?; - let client_validation_ctx = self.ibc_context.get_client_validation_context(); - let client_status = client_state - .status(client_validation_ctx, &client_id) - .map_err(ContextError::from)?; - - Ok(Response::new(QueryClientStatusResponse { - status: format!("{}", client_status), - })) - } - - async fn client_params( - &self, - _request: Request, - ) -> Result, Status> { - Err(Status::unimplemented( - "Querying ClientParams is not supported yet", - )) - } - - async fn upgraded_client_state( - &self, - _request: Request, - ) -> Result, Status> { - let plan = self - .upgrade_context - .upgrade_plan() - .map_err(ClientError::from) - .map_err(ContextError::from)?; - - let upgraded_client_state_path = UpgradeClientPath::UpgradedClientState(plan.height); - - let upgraded_client_state = self - .upgrade_context - .upgraded_client_state(&upgraded_client_state_path) - .map_err(ClientError::from) - .map_err(ContextError::from)?; - - Ok(Response::new(QueryUpgradedClientStateResponse { - upgraded_client_state: Some(upgraded_client_state.into()), - })) - } - - async fn upgraded_consensus_state( - &self, - _request: Request, - ) -> Result, Status> { - let plan = self - .upgrade_context - .upgrade_plan() - .map_err(ClientError::from) - .map_err(ContextError::from)?; - - let upgraded_consensus_state_path = - UpgradeClientPath::UpgradedClientConsensusState(plan.height); - - let upgraded_consensus_state = self - .upgrade_context - .upgraded_consensus_state(&upgraded_consensus_state_path) - .map_err(ClientError::from) - .map_err(ContextError::from)?; - - Ok(Response::new(QueryUpgradedConsensusStateResponse { - upgraded_consensus_state: Some(upgraded_consensus_state.into()), - })) - } -} diff --git a/crates/ibc/src/services/core/connection.rs b/crates/ibc/src/services/core/connection.rs deleted file mode 100644 index eaf77ca65..000000000 --- a/crates/ibc/src/services/core/connection.rs +++ /dev/null @@ -1,227 +0,0 @@ -//! [`ConnectionQueryService`](ConnectionQueryService) takes a generic `I` to store `ibc_context` that implements [`QueryContext`](QueryContext). -//! `I` must be a type where writes from one thread are readable from another. -//! This means using `Arc>` or `Arc>` in most cases. - -use alloc::str::FromStr; -use std::boxed::Box; - -use ibc_proto::google::protobuf::Any; -use ibc_proto::ibc::core::client::v1::IdentifiedClientState; -use ibc_proto::ibc::core::connection::v1::query_server::Query as ConnectionQuery; -use ibc_proto::ibc::core::connection::v1::{ - Params as ConnectionParams, QueryClientConnectionsRequest, QueryClientConnectionsResponse, - QueryConnectionClientStateRequest, QueryConnectionClientStateResponse, - QueryConnectionConsensusStateRequest, QueryConnectionConsensusStateResponse, - QueryConnectionParamsRequest, QueryConnectionParamsResponse, QueryConnectionRequest, - QueryConnectionResponse, QueryConnectionsRequest, QueryConnectionsResponse, -}; -use tonic::{Request, Response, Status}; - -use crate::core::ics24_host::identifier::{ClientId, ConnectionId}; -use crate::core::ics24_host::path::{ - ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, ConnectionPath, Path, -}; -use crate::core::ValidationContext; -use crate::prelude::*; -use crate::services::core::context::QueryContext; -use crate::Height; - -// TODO(rano): currently the services don't support pagination, so we return all the results. - -/// The generic `I` must be a type where writes from one thread are readable from another. -/// This means using `Arc>` or `Arc>` in most cases. -pub struct ConnectionQueryService -where - I: QueryContext + Send + Sync + 'static, - ::AnyClientState: Into, - ::AnyConsensusState: Into, -{ - ibc_context: I, -} - -impl ConnectionQueryService -where - I: QueryContext + Send + Sync + 'static, - ::AnyClientState: Into, - ::AnyConsensusState: Into, -{ - /// The parameter `ibc_context` must be a type where writes from one thread are readable from another. - /// This means using `Arc>` or `Arc>` in most cases. - pub fn new(ibc_context: I) -> Self { - Self { ibc_context } - } -} - -#[tonic::async_trait] -impl ConnectionQuery for ConnectionQueryService -where - I: QueryContext + Send + Sync + 'static, - ::AnyClientState: Into, - ::AnyConsensusState: Into, -{ - async fn connection( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let connection_id = ConnectionId::from_str(request_ref.connection_id.as_str())?; - - let connection_end = self.ibc_context.connection_end(&connection_id)?; - - let current_height = self.ibc_context.host_height()?; - - let proof = self - .ibc_context - .get_proof( - current_height, - &Path::Connection(ConnectionPath::new(&connection_id)), - ) - .ok_or_else(|| { - Status::not_found(format!( - "Proof not found for connection path {}", - connection_id.as_str() - )) - })?; - - Ok(Response::new(QueryConnectionResponse { - connection: Some(connection_end.into()), - proof, - proof_height: Some(current_height.into()), - })) - } - - async fn connections( - &self, - _request: Request, - ) -> Result, Status> { - let connections = self.ibc_context.connection_ends()?; - - Ok(Response::new(QueryConnectionsResponse { - connections: connections.into_iter().map(Into::into).collect(), - height: Some(self.ibc_context.host_height()?.into()), - // no support for pagination yet - pagination: None, - })) - } - - async fn client_connections( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let client_id = ClientId::from_str(request_ref.client_id.as_str())?; - - let connections = self.ibc_context.client_connection_ends(&client_id)?; - - let current_height = self.ibc_context.host_height()?; - - let proof: Vec = self - .ibc_context - .get_proof( - current_height, - &Path::ClientConnection(ClientConnectionPath::new(&client_id)), - ) - .ok_or_else(|| { - Status::not_found(format!( - "Proof not found for client connection path {}", - client_id.as_str() - )) - })?; - - Ok(Response::new(QueryClientConnectionsResponse { - connection_paths: connections.into_iter().map(|x| x.as_str().into()).collect(), - proof, - proof_height: Some(current_height.into()), - })) - } - - async fn connection_client_state( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let connection_id = ConnectionId::from_str(request_ref.connection_id.as_str())?; - - let connection_end = self.ibc_context.connection_end(&connection_id)?; - - let client_state = self.ibc_context.client_state(connection_end.client_id())?; - - let current_height = self.ibc_context.host_height()?; - - let proof = self - .ibc_context - .get_proof( - current_height, - &Path::ClientState(ClientStatePath::new(connection_end.client_id())), - ) - .ok_or_else(|| { - Status::not_found(format!( - "Proof not found for client state path {}", - connection_end.client_id().as_str() - )) - })?; - - Ok(Response::new(QueryConnectionClientStateResponse { - identified_client_state: Some(IdentifiedClientState { - client_id: connection_end.client_id().as_str().into(), - client_state: Some(client_state.into()), - }), - proof, - proof_height: Some(current_height.into()), - })) - } - - async fn connection_consensus_state( - &self, - request: Request, - ) -> Result, Status> { - let request_ref = request.get_ref(); - - let connection_id = ConnectionId::from_str(request_ref.connection_id.as_str())?; - - let connection_end = self.ibc_context.connection_end(&connection_id)?; - - let height = Height::new(request_ref.revision_number, request_ref.revision_height) - .map_err(|e| Status::invalid_argument(e.to_string()))?; - - let consensus_path = ClientConsensusStatePath::new(connection_end.client_id(), &height); - - let consensus_state = self.ibc_context.consensus_state(&consensus_path)?; - - let current_height = self.ibc_context.host_height()?; - - let proof = self - .ibc_context - .get_proof(current_height, &Path::ClientConsensusState(consensus_path)) - .ok_or_else(|| { - Status::not_found(format!( - "Proof not found for consensus state path {}", - connection_end.client_id().as_str() - )) - })?; - - Ok(Response::new(QueryConnectionConsensusStateResponse { - consensus_state: Some(consensus_state.into()), - client_id: connection_end.client_id().as_str().into(), - proof, - proof_height: Some(current_height.into()), - })) - } - - async fn connection_params( - &self, - _request: Request, - ) -> Result, Status> { - Ok(Response::new(QueryConnectionParamsResponse { - params: Some(ConnectionParams { - max_expected_time_per_block: self - .ibc_context - .max_expected_time_per_block() - .as_secs(), - }), - })) - } -} diff --git a/crates/ibc/src/services/error.rs b/crates/ibc/src/services/error.rs deleted file mode 100644 index 433a5e321..000000000 --- a/crates/ibc/src/services/error.rs +++ /dev/null @@ -1,18 +0,0 @@ -use alloc::string::ToString; - -use tonic::Status; - -use crate::core::ics24_host::identifier::IdentifierError; -use crate::core::ContextError; - -impl From for Status { - fn from(err: IdentifierError) -> Self { - Status::invalid_argument(err.to_string()) - } -} - -impl From for Status { - fn from(err: ContextError) -> Self { - Status::not_found(err.to_string()) - } -}