diff --git a/.changelog/v0.50.0/features/1128-derive-schemars-on-some-ibc-core-structs.md b/.changelog/v0.50.0/features/1128-derive-schemars-on-some-ibc-core-structs.md new file mode 100644 index 000000000..9c1693c4e --- /dev/null +++ b/.changelog/v0.50.0/features/1128-derive-schemars-on-some-ibc-core-structs.md @@ -0,0 +1,3 @@ +- [ibc-core] Add `schemars` derivation on some more IBC core structs like + `Status`, `ConnectionEnd` and `ChannelEnd` + ([\#1129](https://github.com/cosmos/ibc-rs/pull/1129)) diff --git a/.changelog/v0.50.0/features/1128-domain-request-response-query-types.md b/.changelog/v0.50.0/features/1128-domain-request-response-query-types.md new file mode 100644 index 000000000..ca903f850 --- /dev/null +++ b/.changelog/v0.50.0/features/1128-domain-request-response-query-types.md @@ -0,0 +1,3 @@ +- [ibc-query] Add domain definitions for request/response query types and their + corresponding conversions to/from protos + ([\#1128](https://github.com/cosmos/ibc-rs/issues/1128)) diff --git a/ibc-core/ics02-client/types/src/status.rs b/ibc-core/ics02-client/types/src/status.rs index 9660758f4..1fb931f23 100644 --- a/ibc-core/ics02-client/types/src/status.rs +++ b/ibc-core/ics02-client/types/src/status.rs @@ -1,4 +1,7 @@ use core::fmt::{Debug, Display, Formatter}; +use core::str::FromStr; + +use ibc_primitives::prelude::*; use crate::error::ClientError; @@ -16,7 +19,9 @@ pub enum UpdateKind { } /// Represents the status of a client -#[derive(Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub enum Status { /// The client is active and allowed to be used Active, @@ -55,3 +60,19 @@ impl Display for Status { write!(f, "{self:?}") } } + +impl FromStr for Status { + type Err = ClientError; + + fn from_str(s: &str) -> Result { + match s { + "ACTIVE" => Ok(Status::Active), + "FROZEN" => Ok(Status::Frozen), + "EXPIRED" => Ok(Status::Expired), + "UNAUTHORIZED" => Ok(Status::Unauthorized), + _ => Err(ClientError::Other { + description: format!("invalid status string: {s}"), + }), + } + } +} diff --git a/ibc-core/ics03-connection/types/src/connection.rs b/ibc-core/ics03-connection/types/src/connection.rs index a1bfb2670..444a27548 100644 --- a/ibc-core/ics03-connection/types/src/connection.rs +++ b/ibc-core/ics03-connection/types/src/connection.rs @@ -30,6 +30,7 @@ use crate::version::Version; derive(borsh::BorshSerialize, borsh::BorshDeserialize) )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct IdentifiedConnectionEnd { pub connection_id: ConnectionId, @@ -100,6 +101,7 @@ impl From for RawIdentifiedConnection { derive(parity_scale_codec::Encode, parity_scale_codec::Decode,) )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct ConnectionEnd { pub state: State, @@ -466,6 +468,7 @@ impl Counterparty { derive(borsh::BorshSerialize, borsh::BorshDeserialize) )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum State { Uninitialized = 0isize, diff --git a/ibc-core/ics04-channel/types/src/channel.rs b/ibc-core/ics04-channel/types/src/channel.rs index 81d4fee84..c8350805c 100644 --- a/ibc-core/ics04-channel/types/src/channel.rs +++ b/ibc-core/ics04-channel/types/src/channel.rs @@ -29,6 +29,7 @@ use crate::Version; derive(borsh::BorshSerialize, borsh::BorshDeserialize) )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[derive(Clone, Debug, PartialEq, Eq)] pub struct IdentifiedChannelEnd { pub port_id: PortId, @@ -107,6 +108,7 @@ impl From for RawIdentifiedChannel { derive(borsh::BorshSerialize, borsh::BorshDeserialize) )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[derive(Clone, Debug, PartialEq, Eq)] pub struct ChannelEnd { pub state: State, @@ -349,6 +351,7 @@ pub(crate) fn verify_connection_hops_length( derive(borsh::BorshSerialize, borsh::BorshDeserialize) )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[derive(Clone, Debug, PartialEq, Eq)] pub struct Counterparty { pub port_id: PortId, @@ -514,6 +517,7 @@ impl FromStr for Order { derive(borsh::BorshSerialize, borsh::BorshDeserialize) )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum State { Uninitialized = 0isize, diff --git a/ibc-core/ics04-channel/types/src/commitment.rs b/ibc-core/ics04-channel/types/src/commitment.rs index dbea7e968..1f1fc464c 100644 --- a/ibc-core/ics04-channel/types/src/commitment.rs +++ b/ibc-core/ics04-channel/types/src/commitment.rs @@ -20,6 +20,7 @@ use crate::timeout::TimeoutHeight; derive(borsh::BorshSerialize, borsh::BorshDeserialize) )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[derive(Clone, Debug, PartialEq, Eq)] pub struct PacketCommitment(Vec); @@ -55,6 +56,7 @@ impl From> for PacketCommitment { derive(borsh::BorshSerialize, borsh::BorshDeserialize) )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[derive(Clone, Debug, PartialEq, Eq)] pub struct AcknowledgementCommitment(Vec); diff --git a/ibc-query/Cargo.toml b/ibc-query/Cargo.toml index 3f6e4e14f..bf6c0de1d 100644 --- a/ibc-query/Cargo.toml +++ b/ibc-query/Cargo.toml @@ -9,19 +9,37 @@ repository = { workspace = true } readme = "README.md" keywords = ["blockchain", "cosmos", "ibc", "rpc", "grpc"] description = """ - Maintained by `ibc-rs`, contains essential IBC query methods and - gRPC query service implementations for the IBC core. + Maintained by `ibc-rs`, contains essential IBC query types, utility functions and + gRPC service implementations for the IBC core. """ [dependencies] # external dependencies displaydoc = { version = "0.2", default-features = false } -tonic = "0.10" +schemars = { workspace = true , optional = true } +serde = { workspace = true, optional = true } +tonic = "0.10" # ibc dependencies -ibc = { workspace = true } +ibc = { workspace = true } ibc-proto = { workspace = true, features = ["server"] } [features] default = ["std"] -std = ["ibc-proto/std", "ibc/std"] +std = [ + "ibc/std", + "ibc-proto/std", +] +serde = [ + "dep:serde", + "ibc/serde", + "ibc-proto/serde", +] +schema = [ + "dep:schemars", + "ibc/schema", + "ibc-proto/json-schema", + "serde", + "std", +] + diff --git a/ibc-query/README.md b/ibc-query/README.md index 80117ebc8..c5a5f684e 100644 --- a/ibc-query/README.md +++ b/ibc-query/README.md @@ -1,7 +1,33 @@ # IBC Query -This crate provides a set of utility traits and implementations for querying the -state of an [`ibc-rs`](https://github.com/cosmos/ibc-rs) enabled chain. +## Overview -Implementations contain essential IBC query methods and gRPC query services for -each of the IBC core client, connection, and channel layers. +This crate offers a comprehensive set of utility types, traits, and functions +designed for integrating either a gRPC query server or implementing RPC methods +in hosts. It specifically facilitates querying the state of the IBC core client, +connection, and channel layers of a chain enabled with `ibc-rs`. + +## Features + +- Provides essential utility request/response domain types and their conversions +to the proto types for efficient integration. +- Provides convenient query objects with pre-implemented gRPC query services. +- Offers convenient objects on which query service has been implemented and +- Includes convenient `QueryContext` and `ProvableContext` traits that extend + the capabilities of an implemented IBC module, enabling the retrieval of state + from the chain. +- Derives `serde` and `schema` for all the domain types enabling easy + (de)serialization. This feature is particularly beneficial for JSON RPC + implementations. + +## Remarks + +- At present, the Protobuf representation of request types does not include + support for querying at a specific height. Consequently, the current state of + `ibc-query` allows conversion from protos as a compatible direction but does + not support conversion into protos due to the absence of the `query_height` + fields. + +- Currently `ibc-query` does not support pagination. If pagination is a + requirement for your project, please open an issue and provide details about + your usage. diff --git a/ibc-query/src/core/channel/mod.rs b/ibc-query/src/core/channel/mod.rs index b24e36180..7cdb244ce 100644 --- a/ibc-query/src/core/channel/mod.rs +++ b/ibc-query/src/core/channel/mod.rs @@ -1,5 +1,7 @@ mod query; mod service; +mod types; pub use query::*; pub use service::*; +pub use types::*; diff --git a/ibc-query/src/core/channel/query.rs b/ibc-query/src/core/channel/query.rs index f0e64e80b..ff39fec72 100644 --- a/ibc-query/src/core/channel/query.rs +++ b/ibc-query/src/core/channel/query.rs @@ -1,18 +1,15 @@ //! Provides utility functions for querying IBC channel states. -use alloc::format; -use core::str::FromStr; - use ibc::core::client::context::ClientValidationContext; -use ibc::core::client::types::Height; -use ibc::core::host::types::identifiers::{ChannelId, ConnectionId, PortId, Sequence}; use ibc::core::host::types::path::{ AckPath, ChannelEndPath, ClientConsensusStatePath, ClientStatePath, CommitmentPath, Path, ReceiptPath, SeqRecvPath, SeqSendPath, }; use ibc::core::host::{ConsensusStateRef, ValidationContext}; +use ibc::primitives::prelude::format; use ibc_proto::google::protobuf::Any; -use ibc_proto::ibc::core::channel::v1::{ + +use super::{ QueryChannelClientStateRequest, QueryChannelClientStateResponse, QueryChannelConsensusStateRequest, QueryChannelConsensusStateResponse, QueryChannelRequest, QueryChannelResponse, QueryChannelsRequest, QueryChannelsResponse, @@ -25,8 +22,7 @@ use ibc_proto::ibc::core::channel::v1::{ QueryPacketReceiptRequest, QueryPacketReceiptResponse, QueryUnreceivedAcksRequest, QueryUnreceivedAcksResponse, QueryUnreceivedPacketsRequest, QueryUnreceivedPacketsResponse, }; -use ibc_proto::ibc::core::client::v1::IdentifiedClientState; - +use crate::core::client::IdentifiedClientState; use crate::core::context::{ProvableContext, QueryContext}; use crate::error::QueryError; @@ -39,29 +35,24 @@ pub fn query_channel( 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_path = ChannelEndPath::new(&request.port_id, &request.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_or_else(|| { + QueryError::proof_not_found(format!( + "Proof not found for channel end path {channel_end_path:?}" + )) })?; - Ok(QueryChannelResponse { - channel: Some(channel_end.into()), + Ok(QueryChannelResponse::new( + channel_end, proof, - proof_height: Some(current_height.into()), - }) + current_height, + )) } /// Queries for all existing IBC channels and returns the corresponding channel ends @@ -74,12 +65,11 @@ where { 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, - }) + Ok(QueryChannelsResponse::new( + channel_ends, + ibc_ctx.host_height()?, + None, + )) } /// Queries for all channels associated with a given connection @@ -90,8 +80,6 @@ pub fn query_connection_channels( 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 @@ -101,17 +89,16 @@ where .channel_end .connection_hops() .iter() - .any(|connection_hop| connection_hop == &connection_id) + .any(|connection_hop| connection_hop == &request.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, - }) + Ok(QueryConnectionChannelsResponse::new( + connection_channel_ends, + ibc_ctx.host_height()?, + None, + )) } /// Queries for the client state associated with a channel by the given channel @@ -123,11 +110,7 @@ pub fn query_channel_client_state( 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 channel_end_path = ChannelEndPath::new(&request.port_id, &request.channel_id); let channel_end = ibc_ctx.channel_end(&channel_end_path)?; @@ -135,8 +118,11 @@ where .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), + .ok_or_else(|| { + QueryError::proof_not_found(format!( + "Channel {} does not have a connection", + request.channel_id + )) })??; let client_val_ctx = ibc_ctx.get_client_validation_context(); @@ -150,21 +136,18 @@ where current_height, &Path::ClientState(ClientStatePath::new(connection_end.client_id().clone())), ) - .ok_or(QueryError::ProofNotFound { - description: format!( + .ok_or_else(|| { + QueryError::proof_not_found(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()), - }), + Ok(QueryChannelClientStateResponse::new( + IdentifiedClientState::new(connection_end.client_id().clone(), client_state.into()), proof, - proof_height: Some(current_height.into()), - }) + current_height, + )) } /// Queries for the consensus state associated with a channel by the given @@ -177,13 +160,7 @@ where I: QueryContext, ConsensusStateRef: 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_path = ChannelEndPath::new(&request.port_id, &request.channel_id); let channel_end = ibc_ctx.channel_end(&channel_end_path)?; @@ -191,14 +168,17 @@ where .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), + .ok_or_else(|| { + QueryError::proof_not_found(format!( + "Channel {} does not have a connection", + request.channel_id + )) })??; let consensus_path = ClientConsensusStatePath::new( connection_end.client_id().clone(), - height.revision_number(), - height.revision_height(), + request.consensus_height.revision_number(), + request.consensus_height.revision_height(), ); let client_val_ctx = ibc_ctx.get_client_validation_context(); @@ -211,19 +191,18 @@ where current_height, &Path::ClientConsensusState(consensus_path.clone()), ) - .ok_or(QueryError::ProofNotFound { - description: format!( - "Proof not found for client consensus state path: {:?}", - consensus_path - ), + .ok_or_else(|| { + QueryError::proof_not_found(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()), + Ok(QueryChannelConsensusStateResponse::new( + consensus_state.into(), + connection_end.client_id().clone(), proof, - proof_height: Some(current_height.into()), - }) + current_height, + )) } /// Queries for the packet commitment associated with a channel by the given @@ -235,13 +214,8 @@ pub fn query_packet_commitment( 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 commitment_path = + CommitmentPath::new(&request.port_id, &request.channel_id, request.sequence); let packet_commitment_data = ibc_ctx.get_packet_commitment(&commitment_path)?; @@ -249,18 +223,17 @@ where 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_or_else(|| { + QueryError::proof_not_found(format!( + "Proof not found for packet commitment path: {commitment_path:?}" + )) })?; - Ok(QueryPacketCommitmentResponse { - commitment: packet_commitment_data.into_vec(), + Ok(QueryPacketCommitmentResponse::new( + packet_commitment_data, proof, - proof_height: Some(current_height.into()), - }) + current_height, + )) } /// Queries for all packet commitments associated with a channel @@ -271,11 +244,7 @@ pub fn query_packet_commitments( 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 channel_end_path = ChannelEndPath::new(&request.port_id, &request.channel_id); let commitments = ibc_ctx .packet_commitments(&channel_end_path)? @@ -283,12 +252,11 @@ where .map(Into::into) .collect(); - Ok(QueryPacketCommitmentsResponse { + Ok(QueryPacketCommitmentsResponse::new( commitments, - height: Some(ibc_ctx.host_height()?.into()), - // no support for pagination yet - pagination: None, - }) + ibc_ctx.host_height()?, + None, + )) } /// Queries for the packet receipt associated with a channel by the given @@ -300,13 +268,7 @@ pub fn query_packet_receipt( 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); + let receipt_path = ReceiptPath::new(&request.port_id, &request.channel_id, request.sequence); // Receipt only has one enum // Unreceived packets are not stored @@ -316,18 +278,17 @@ where 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_or_else(|| { + QueryError::proof_not_found(format!( + "Proof not found for packet receipt path: {receipt_path:?}" + )) })?; - Ok(QueryPacketReceiptResponse { - received: packet_receipt_data.is_ok(), + Ok(QueryPacketReceiptResponse::new( + packet_receipt_data.is_ok(), proof, - proof_height: Some(current_height.into()), - }) + current_height, + )) } /// Queries for the packet acknowledgement associated with a channel by the @@ -339,13 +300,8 @@ pub fn query_packet_acknowledgement( 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 acknowledgement_path = + AckPath::new(&request.port_id, &request.channel_id, request.sequence); let packet_acknowledgement_data = ibc_ctx.get_packet_acknowledgement(&acknowledgement_path)?; @@ -353,18 +309,17 @@ where 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_or_else(|| { + QueryError::proof_not_found(format!( + "Proof not found for packet acknowledgement path: {acknowledgement_path:?}" + )) })?; - Ok(QueryPacketAcknowledgementResponse { - acknowledgement: packet_acknowledgement_data.into_vec(), + Ok(QueryPacketAcknowledgementResponse::new( + packet_acknowledgement_data, proof, - proof_height: Some(current_height.into()), - }) + current_height, + )) } /// Queries for all packet acknowledgements associated with a channel @@ -375,17 +330,13 @@ pub fn query_packet_acknowledgements( 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); + .map(Into::into); - let channel_end_path = ChannelEndPath::new(&port_id, &channel_id); + let channel_end_path = ChannelEndPath::new(&request.port_id, &request.channel_id); let acknowledgements = ibc_ctx .packet_acknowledgements(&channel_end_path, commitment_sequences)? @@ -393,12 +344,11 @@ where .map(Into::into) .collect(); - Ok(QueryPacketAcknowledgementsResponse { + Ok(QueryPacketAcknowledgementsResponse::new( acknowledgements, - height: Some(ibc_ctx.host_height()?.into()), - // no support for pagination yet - pagination: None, - }) + ibc_ctx.host_height()?, + None, + )) } /// Queries for all unreceived packets associated with a channel @@ -409,24 +359,20 @@ pub fn query_unreceived_packets( 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); + .map(Into::into); - let channel_end_path = ChannelEndPath::new(&port_id, &channel_id); + let channel_end_path = ChannelEndPath::new(&request.port_id, &request.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()), - }) + Ok(QueryUnreceivedPacketsResponse::new( + unreceived_packets, + ibc_ctx.host_height()?, + )) } /// Queries for all unreceived acknowledgements associated with a channel @@ -437,24 +383,16 @@ pub fn query_unreceived_acks( where I: QueryContext, { - let channel_id = ChannelId::from_str(request.channel_id.as_str())?; + let sequences = request.packet_ack_sequences.iter().copied().map(Into::into); - 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 channel_end_path = ChannelEndPath::new(&request.port_id, &request.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()), - }) + Ok(QueryUnreceivedAcksResponse::new( + unreceived_acks, + ibc_ctx.host_height()?, + )) } /// Queries for the next sequence to send for the channel specified @@ -466,11 +404,7 @@ pub fn query_next_sequence_send( 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_seq_send_path = SeqSendPath::new(&request.port_id, &request.channel_id); let next_sequence_send = ibc_ctx.get_next_sequence_send(&next_seq_send_path)?; @@ -478,18 +412,18 @@ where let proof = ibc_ctx .get_proof(current_height, &Path::SeqSend(next_seq_send_path)) - .ok_or(QueryError::ProofNotFound { - description: format!( + .ok_or_else(|| { + QueryError::proof_not_found(format!( "Next sequence send proof not found for channel {}", - channel_id - ), + request.channel_id + )) })?; - Ok(QueryNextSequenceSendResponse { - next_sequence_send: next_sequence_send.into(), + Ok(QueryNextSequenceSendResponse::new( + next_sequence_send, proof, - proof_height: Some(current_height.into()), - }) + current_height, + )) } /// Queries for the next sequence receive associated with a channel @@ -500,11 +434,7 @@ pub fn query_next_sequence_receive( 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_seq_recv_path = SeqRecvPath::new(&request.port_id, &request.channel_id); let next_sequence_recv = ibc_ctx.get_next_sequence_recv(&next_seq_recv_path)?; @@ -512,16 +442,16 @@ where let proof = ibc_ctx .get_proof(current_height, &Path::SeqRecv(next_seq_recv_path)) - .ok_or(QueryError::ProofNotFound { - description: format!( + .ok_or_else(|| { + QueryError::proof_not_found(format!( "Next sequence receive proof not found for channel {}", - channel_id - ), + request.channel_id + )) })?; - Ok(QueryNextSequenceReceiveResponse { - next_sequence_receive: next_sequence_recv.into(), + Ok(QueryNextSequenceReceiveResponse::new( + next_sequence_recv, proof, - proof_height: Some(current_height.into()), - }) + current_height, + )) } diff --git a/ibc-query/src/core/channel/service.rs b/ibc-query/src/core/channel/service.rs index 05d64428b..9c4f7fa26 100644 --- a/ibc-query/src/core/channel/service.rs +++ b/ibc-query/src/core/channel/service.rs @@ -31,6 +31,7 @@ use super::{ query_unreceived_packets, }; use crate::core::context::QueryContext; +use crate::utils::{IntoDomain, IntoResponse, TryIntoDomain}; // TODO(rano): currently the services don't support pagination, so we return all the results. @@ -66,81 +67,65 @@ where &self, request: Request, ) -> Result, Status> { - let response = query_channel(&self.ibc_context, request.get_ref())?; - - Ok(Response::new(response)) + query_channel(&self.ibc_context, &request.try_into_domain()?)?.into_response() } async fn channels( &self, request: Request, ) -> Result, Status> { - let response = query_channels(&self.ibc_context, request.get_ref())?; - - Ok(Response::new(response)) + query_channels(&self.ibc_context, &request.into_domain())?.into_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)) + query_connection_channels(&self.ibc_context, &request.try_into_domain()?)?.into_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)) + query_channel_client_state(&self.ibc_context, &request.try_into_domain()?)?.into_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)) + query_channel_consensus_state(&self.ibc_context, &request.try_into_domain()?)? + .into_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)) + query_packet_commitment(&self.ibc_context, &request.try_into_domain()?)?.into_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)) + query_packet_commitments(&self.ibc_context, &request.try_into_domain()?)?.into_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)) + query_packet_receipt(&self.ibc_context, &request.try_into_domain()?)?.into_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)) + query_packet_acknowledgement(&self.ibc_context, &request.try_into_domain()?)? + .into_response() } /// Returns all the acknowledgements if sequences is omitted. @@ -148,9 +133,8 @@ where &self, request: Request, ) -> Result, Status> { - let response = query_packet_acknowledgements(&self.ibc_context, request.get_ref())?; - - Ok(Response::new(response)) + query_packet_acknowledgements(&self.ibc_context, &request.try_into_domain()?)? + .into_response() } /// Returns all the unreceived packets if sequences is omitted. @@ -158,9 +142,7 @@ where &self, request: Request, ) -> Result, Status> { - let response = query_unreceived_packets(&self.ibc_context, request.get_ref())?; - - Ok(Response::new(response)) + query_unreceived_packets(&self.ibc_context, &request.try_into_domain()?)?.into_response() } /// Returns all the unreceived acknowledgements if sequences is omitted. @@ -168,27 +150,21 @@ where &self, request: Request, ) -> Result, Status> { - let response = query_unreceived_acks(&self.ibc_context, request.get_ref())?; - - Ok(Response::new(response)) + query_unreceived_acks(&self.ibc_context, &request.try_into_domain()?)?.into_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)) + query_next_sequence_receive(&self.ibc_context, &request.try_into_domain()?)?.into_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)) + query_next_sequence_send(&self.ibc_context, &request.try_into_domain()?)?.into_response() } async fn upgrade_error( diff --git a/ibc-query/src/core/channel/types/mod.rs b/ibc-query/src/core/channel/types/mod.rs new file mode 100644 index 000000000..b8be6322b --- /dev/null +++ b/ibc-query/src/core/channel/types/mod.rs @@ -0,0 +1,5 @@ +mod request; +mod response; + +pub use request::*; +pub use response::*; diff --git a/ibc-query/src/core/channel/types/request.rs b/ibc-query/src/core/channel/types/request.rs new file mode 100644 index 000000000..d626df5d9 --- /dev/null +++ b/ibc-query/src/core/channel/types/request.rs @@ -0,0 +1,410 @@ +//! Contains all the RPC method request domain types and their conversions to +//! and from the corresponding gRPC proto types for the channel module. + +use ibc::core::client::types::Height; +use ibc::core::host::types::identifiers::{ChannelId, ConnectionId, PortId, Sequence}; +use ibc::primitives::prelude::*; +use ibc_proto::ibc::core::channel::v1::{ + QueryChannelClientStateRequest as RawQueryChannelClientStateRequest, + QueryChannelConsensusStateRequest as RawQueryChannelConsensusStateRequest, + QueryChannelRequest as RawQueryChannelRequest, QueryChannelsRequest as RawQueryChannelsRequest, + QueryConnectionChannelsRequest as RawQueryConnectionChannelsRequest, + QueryNextSequenceReceiveRequest as RawQueryNextSequenceReceiveRequest, + QueryNextSequenceSendRequest as RawQueryNextSequenceSendRequest, + QueryPacketAcknowledgementRequest as RawQueryPacketAcknowledgementRequest, + QueryPacketAcknowledgementsRequest as RawQueryPacketAcknowledgementsRequest, + QueryPacketCommitmentRequest as RawQueryPacketCommitmentRequest, + QueryPacketCommitmentsRequest as RawQueryPacketCommitmentsRequest, + QueryPacketReceiptRequest as RawQueryPacketReceiptRequest, + QueryUnreceivedAcksRequest as RawQueryUnreceivedAcksRequest, + QueryUnreceivedPacketsRequest as RawQueryUnreceivedPacketsRequest, +}; + +use crate::error::QueryError; +use crate::types::PageRequest; + +/// Defines the RPC method request type for querying a channel +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryChannelRequest { + pub port_id: PortId, + pub channel_id: ChannelId, + pub query_height: Option, +} + +impl TryFrom for QueryChannelRequest { + type Error = QueryError; + + fn try_from(request: RawQueryChannelRequest) -> Result { + Ok(Self { + port_id: request.port_id.parse()?, + channel_id: request.channel_id.parse()?, + query_height: None, + }) + } +} + +/// Defines the RPC method request type for querying all channels +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryChannelsRequest { + pub pagination: Option, +} + +impl From for QueryChannelsRequest { + fn from(request: RawQueryChannelsRequest) -> Self { + Self { + pagination: request.pagination.map(Into::into), + } + } +} + +/// Defines the RPC method request type for querying all channels associated +/// with a connection identifier +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConnectionChannelsRequest { + pub connection_id: ConnectionId, + pub pagination: Option, +} + +impl TryFrom for QueryConnectionChannelsRequest { + type Error = QueryError; + + fn try_from(request: RawQueryConnectionChannelsRequest) -> Result { + Ok(Self { + connection_id: request.connection.parse()?, + pagination: request.pagination.map(Into::into), + }) + } +} + +/// Defines the RPC method request type for querying the client state associated +/// with a channel +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryChannelClientStateRequest { + pub port_id: PortId, + pub channel_id: ChannelId, + pub query_height: Option, +} + +impl TryFrom for QueryChannelClientStateRequest { + type Error = QueryError; + + fn try_from(request: RawQueryChannelClientStateRequest) -> Result { + Ok(Self { + port_id: request.port_id.parse()?, + channel_id: request.channel_id.parse()?, + query_height: None, + }) + } +} + +/// Defines the RPC method request type for querying the consensus state +/// associated with a channel +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryChannelConsensusStateRequest { + pub port_id: PortId, + pub channel_id: ChannelId, + pub consensus_height: Height, + pub query_height: Option, +} + +impl TryFrom for QueryChannelConsensusStateRequest { + type Error = QueryError; + + fn try_from(request: RawQueryChannelConsensusStateRequest) -> Result { + Ok(Self { + port_id: request.port_id.parse()?, + channel_id: request.channel_id.parse()?, + consensus_height: Height::new(request.revision_number, request.revision_height)?, + query_height: None, + }) + } +} + +/// Defines the RPC method request type for querying the packet commitment +/// associated with the specified channel +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryPacketCommitmentRequest { + pub port_id: PortId, + pub channel_id: ChannelId, + pub sequence: Sequence, + pub query_height: Option, +} + +impl TryFrom for QueryPacketCommitmentRequest { + type Error = QueryError; + + fn try_from(request: RawQueryPacketCommitmentRequest) -> Result { + Ok(Self { + port_id: request.port_id.parse()?, + channel_id: request.channel_id.parse()?, + sequence: request.sequence.into(), + query_height: None, + }) + } +} + +/// Defines the RPC method request type for querying all packet commitments +/// associated with the specified channel +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryPacketCommitmentsRequest { + pub port_id: PortId, + pub channel_id: ChannelId, + pub pagination: Option, +} + +impl TryFrom for QueryPacketCommitmentsRequest { + type Error = QueryError; + + fn try_from(request: RawQueryPacketCommitmentsRequest) -> Result { + Ok(Self { + port_id: request.port_id.parse()?, + channel_id: request.channel_id.parse()?, + pagination: request.pagination.map(Into::into), + }) + } +} + +impl From for RawQueryPacketCommitmentsRequest { + fn from(request: QueryPacketCommitmentsRequest) -> Self { + Self { + port_id: request.port_id.to_string(), + channel_id: request.channel_id.to_string(), + pagination: request.pagination.map(Into::into), + } + } +} + +/// Defines the RPC method request type for querying the packet receipt +/// associated with the specified channel and sequence number +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryPacketReceiptRequest { + pub port_id: PortId, + pub channel_id: ChannelId, + pub sequence: Sequence, + pub query_height: Option, +} + +impl TryFrom for QueryPacketReceiptRequest { + type Error = QueryError; + + fn try_from(request: RawQueryPacketReceiptRequest) -> Result { + Ok(Self { + port_id: request.port_id.parse()?, + channel_id: request.channel_id.parse()?, + sequence: request.sequence.into(), + query_height: None, + }) + } +} + +/// Defines the RPC method request type for querying the packet acknowledgement +/// associated with the specified channel and sequence number +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryPacketAcknowledgementRequest { + pub port_id: PortId, + pub channel_id: ChannelId, + pub sequence: Sequence, + pub query_height: Option, +} + +impl TryFrom for QueryPacketAcknowledgementRequest { + type Error = QueryError; + + fn try_from(request: RawQueryPacketAcknowledgementRequest) -> Result { + Ok(Self { + port_id: request.port_id.parse()?, + channel_id: request.channel_id.parse()?, + sequence: request.sequence.into(), + query_height: None, + }) + } +} + +/// Defines the RPC method request type for querying the packet acknowledgements +/// associated with the specified channel +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryPacketAcknowledgementsRequest { + pub port_id: PortId, + pub channel_id: ChannelId, + pub packet_commitment_sequences: Vec, + pub pagination: Option, +} + +impl TryFrom for QueryPacketAcknowledgementsRequest { + type Error = QueryError; + + fn try_from(request: RawQueryPacketAcknowledgementsRequest) -> Result { + Ok(Self { + port_id: request.port_id.parse()?, + channel_id: request.channel_id.parse()?, + packet_commitment_sequences: request + .packet_commitment_sequences + .into_iter() + .map(Into::into) + .collect(), + pagination: request.pagination.map(Into::into), + }) + } +} + +impl From for RawQueryPacketAcknowledgementsRequest { + fn from(request: QueryPacketAcknowledgementsRequest) -> Self { + Self { + port_id: request.port_id.to_string(), + channel_id: request.channel_id.to_string(), + packet_commitment_sequences: request + .packet_commitment_sequences + .into_iter() + .map(Into::into) + .collect(), + pagination: request.pagination.map(Into::into), + } + } +} + +/// Defines the RPC method request type for querying the unreceived packets +/// associated with the specified channel +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryUnreceivedPacketsRequest { + pub port_id: PortId, + pub channel_id: ChannelId, + pub packet_commitment_sequences: Vec, +} + +impl TryFrom for QueryUnreceivedPacketsRequest { + type Error = QueryError; + + fn try_from(request: RawQueryUnreceivedPacketsRequest) -> Result { + Ok(Self { + port_id: request.port_id.parse()?, + channel_id: request.channel_id.parse()?, + packet_commitment_sequences: request + .packet_commitment_sequences + .into_iter() + .map(Into::into) + .collect(), + }) + } +} + +impl From for RawQueryUnreceivedPacketsRequest { + fn from(request: QueryUnreceivedPacketsRequest) -> Self { + Self { + port_id: request.port_id.to_string(), + channel_id: request.channel_id.to_string(), + packet_commitment_sequences: request + .packet_commitment_sequences + .into_iter() + .map(Into::into) + .collect(), + } + } +} + +/// gRPC query to fetch the unreceived acknowledgements sequences associated with +/// the specified channel. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryUnreceivedAcksRequest { + pub port_id: PortId, + pub channel_id: ChannelId, + pub packet_ack_sequences: Vec, +} + +impl TryFrom for QueryUnreceivedAcksRequest { + type Error = QueryError; + + fn try_from(request: RawQueryUnreceivedAcksRequest) -> Result { + Ok(Self { + port_id: request.port_id.parse()?, + channel_id: request.channel_id.parse()?, + packet_ack_sequences: request + .packet_ack_sequences + .into_iter() + .map(Into::into) + .collect(), + }) + } +} + +impl From for RawQueryUnreceivedAcksRequest { + fn from(request: QueryUnreceivedAcksRequest) -> Self { + Self { + port_id: request.port_id.to_string(), + channel_id: request.channel_id.to_string(), + packet_ack_sequences: request + .packet_ack_sequences + .into_iter() + .map(Into::into) + .collect(), + } + } +} + +/// Defines the RPC method request type for querying the next sequence receive +/// number for the specified channel +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryNextSequenceReceiveRequest { + pub port_id: PortId, + pub channel_id: ChannelId, + pub query_height: Option, +} + +impl TryFrom for QueryNextSequenceReceiveRequest { + type Error = QueryError; + + fn try_from(request: RawQueryNextSequenceReceiveRequest) -> Result { + Ok(Self { + port_id: request.port_id.parse()?, + channel_id: request.channel_id.parse()?, + query_height: None, + }) + } +} +/// Defines the RPC method request type for querying the next sequence send +/// number for the specified channel +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryNextSequenceSendRequest { + pub port_id: PortId, + pub channel_id: ChannelId, + pub query_height: Option, +} + +impl TryFrom for QueryNextSequenceSendRequest { + type Error = QueryError; + + fn try_from(request: RawQueryNextSequenceSendRequest) -> Result { + Ok(Self { + port_id: request.port_id.parse()?, + channel_id: request.channel_id.parse()?, + query_height: None, + }) + } +} diff --git a/ibc-query/src/core/channel/types/response.rs b/ibc-query/src/core/channel/types/response.rs new file mode 100644 index 000000000..8b4f3aa54 --- /dev/null +++ b/ibc-query/src/core/channel/types/response.rs @@ -0,0 +1,738 @@ +//! Contains all the RPC method response domain types and their conversions to +//! and from the corresponding gRPC proto types for the channel module. + +use ibc::core::channel::types::channel::{ChannelEnd, IdentifiedChannelEnd}; +use ibc::core::channel::types::commitment::{AcknowledgementCommitment, PacketCommitment}; +use ibc::core::channel::types::packet::PacketState; +use ibc::core::client::types::Height; +use ibc::core::host::types::identifiers::{ClientId, Sequence}; +use ibc::core::primitives::proto::Any; +use ibc::primitives::prelude::*; +use ibc::primitives::proto::Protobuf; +use ibc_proto::ibc::core::channel::v1::{ + QueryChannelClientStateResponse as RawQueryChannelClientStateResponse, + QueryChannelConsensusStateResponse as RawQueryChannelConsensusStateResponse, + QueryChannelResponse as RawQueryChannelResponse, + QueryChannelsResponse as RawQueryChannelsResponse, + QueryConnectionChannelsResponse as RawQueryConnectionChannelsResponse, + QueryNextSequenceReceiveResponse as RawQueryNextSequenceReceiveResponse, + QueryNextSequenceSendResponse as RawQueryNextSequenceSendResponse, + QueryPacketAcknowledgementResponse as RawQueryPacketAcknowledgementResponse, + QueryPacketAcknowledgementsResponse as RawQueryPacketAcknowledgementsResponse, + QueryPacketCommitmentResponse as RawQueryPacketCommitmentResponse, + QueryPacketCommitmentsResponse as RawQueryPacketCommitmentsResponse, + QueryPacketReceiptResponse as RawQueryPacketReceiptResponse, + QueryUnreceivedAcksResponse as RawQueryUnreceivedAcksResponse, + QueryUnreceivedPacketsResponse as RawQueryUnreceivedPacketsResponse, +}; + +use crate::core::client::IdentifiedClientState; +use crate::error::QueryError; +use crate::types::{PageResponse, Proof}; + +/// Defines the RPC method response type when querying a channel. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryChannelResponse { + pub channel: ChannelEnd, + pub proof: Proof, + pub proof_height: Height, +} + +impl QueryChannelResponse { + pub fn new(channel: ChannelEnd, proof: Proof, proof_height: Height) -> Self { + Self { + channel, + proof, + proof_height, + } + } +} + +impl Protobuf for QueryChannelResponse {} + +impl TryFrom for QueryChannelResponse { + type Error = QueryError; + + fn try_from(value: RawQueryChannelResponse) -> Result { + Ok(Self { + channel: value + .channel + .ok_or_else(|| QueryError::missing_field("channel"))? + .try_into()?, + proof: value.proof, + proof_height: value + .proof_height + .ok_or_else(|| QueryError::missing_field("proof_height"))? + .try_into()?, + }) + } +} + +impl From for RawQueryChannelResponse { + fn from(response: QueryChannelResponse) -> Self { + Self { + channel: Some(response.channel.into()), + proof: response.proof, + proof_height: Some(response.proof_height.into()), + } + } +} + +/// Defines the RPC method response type when querying a list of channels. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryChannelsResponse { + pub channels: Vec, + pub query_height: Height, + pub pagination: Option, +} + +impl QueryChannelsResponse { + pub fn new( + channels: Vec, + query_height: Height, + pagination: Option, + ) -> Self { + Self { + channels, + query_height, + pagination, + } + } +} + +impl Protobuf for QueryChannelsResponse {} + +impl TryFrom for QueryChannelsResponse { + type Error = QueryError; + + fn try_from(value: RawQueryChannelsResponse) -> Result { + Ok(Self { + channels: value + .channels + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + query_height: value + .height + .ok_or_else(|| QueryError::missing_field("height"))? + .try_into()?, + pagination: value.pagination.map(Into::into), + }) + } +} + +impl From for RawQueryChannelsResponse { + fn from(response: QueryChannelsResponse) -> Self { + Self { + channels: response.channels.into_iter().map(Into::into).collect(), + height: Some(response.query_height.into()), + pagination: response.pagination.map(Into::into), + } + } +} + +/// Defines the RPC method response type when querying a list of channels associated with a connection. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConnectionChannelsResponse { + pub channels: Vec, + pub query_height: Height, + pub pagination: Option, +} + +impl QueryConnectionChannelsResponse { + pub fn new( + channels: Vec, + query_height: Height, + pagination: Option, + ) -> Self { + Self { + channels, + query_height, + pagination, + } + } +} + +impl Protobuf for QueryConnectionChannelsResponse {} + +impl TryFrom for QueryConnectionChannelsResponse { + type Error = QueryError; + + fn try_from(value: RawQueryConnectionChannelsResponse) -> Result { + Ok(Self { + channels: value + .channels + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + query_height: value + .height + .ok_or_else(|| QueryError::missing_field("height"))? + .try_into()?, + pagination: value.pagination.map(Into::into), + }) + } +} + +impl From for RawQueryConnectionChannelsResponse { + fn from(response: QueryConnectionChannelsResponse) -> Self { + Self { + channels: response.channels.into_iter().map(Into::into).collect(), + height: Some(response.query_height.into()), + pagination: response.pagination.map(Into::into), + } + } +} + +/// Defines the RPC method response type when querying a channel client state. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryChannelClientStateResponse { + pub identified_client_state: IdentifiedClientState, + pub proof: Proof, + pub proof_height: Height, +} + +impl QueryChannelClientStateResponse { + pub fn new( + identified_client_state: IdentifiedClientState, + proof: Proof, + proof_height: Height, + ) -> Self { + Self { + identified_client_state, + proof, + proof_height, + } + } +} + +impl Protobuf for QueryChannelClientStateResponse {} + +impl TryFrom for QueryChannelClientStateResponse { + type Error = QueryError; + + fn try_from(value: RawQueryChannelClientStateResponse) -> Result { + Ok(Self { + identified_client_state: value + .identified_client_state + .ok_or_else(|| QueryError::missing_field("identified_client_state"))? + .try_into()?, + proof: value.proof, + proof_height: value + .proof_height + .ok_or_else(|| QueryError::missing_field("proof_height"))? + .try_into()?, + }) + } +} + +impl From for RawQueryChannelClientStateResponse { + fn from(response: QueryChannelClientStateResponse) -> Self { + Self { + identified_client_state: Some(response.identified_client_state.into()), + proof: response.proof, + proof_height: Some(response.proof_height.into()), + } + } +} + +/// Defines the RPC method response when for querying a channel consensus state. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryChannelConsensusStateResponse { + pub consensus_state: Any, + pub client_id: ClientId, + pub proof: Proof, + pub proof_height: Height, +} + +impl QueryChannelConsensusStateResponse { + pub fn new( + consensus_state: Any, + client_id: ClientId, + proof: Proof, + proof_height: Height, + ) -> Self { + Self { + consensus_state, + client_id, + proof, + proof_height, + } + } +} + +impl Protobuf for QueryChannelConsensusStateResponse {} + +impl TryFrom for QueryChannelConsensusStateResponse { + type Error = QueryError; + + fn try_from(value: RawQueryChannelConsensusStateResponse) -> Result { + Ok(Self { + consensus_state: value + .consensus_state + .ok_or_else(|| QueryError::missing_field("consensus_state"))?, + client_id: value.client_id.parse()?, + proof: value.proof, + proof_height: value + .proof_height + .ok_or_else(|| QueryError::missing_field("proof_height"))? + .try_into()?, + }) + } +} + +impl From for RawQueryChannelConsensusStateResponse { + fn from(response: QueryChannelConsensusStateResponse) -> Self { + Self { + consensus_state: Some(response.consensus_state), + client_id: response.client_id.to_string(), + proof: response.proof, + proof_height: Some(response.proof_height.into()), + } + } +} + +/// Defines the RPC method response type when querying a packet commitment. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryPacketCommitmentResponse { + pub packet_commitment: PacketCommitment, + pub proof: Proof, + pub proof_height: Height, +} + +impl QueryPacketCommitmentResponse { + pub fn new(packet_commitment: PacketCommitment, proof: Proof, proof_height: Height) -> Self { + Self { + packet_commitment, + proof, + proof_height, + } + } +} + +impl Protobuf for QueryPacketCommitmentResponse {} + +impl TryFrom for QueryPacketCommitmentResponse { + type Error = QueryError; + + fn try_from(value: RawQueryPacketCommitmentResponse) -> Result { + Ok(Self { + packet_commitment: value.commitment.into(), + proof: value.proof, + proof_height: value + .proof_height + .ok_or_else(|| QueryError::missing_field("proof_height"))? + .try_into()?, + }) + } +} + +impl From for RawQueryPacketCommitmentResponse { + fn from(response: QueryPacketCommitmentResponse) -> Self { + Self { + commitment: response.packet_commitment.into_vec(), + proof: response.proof, + proof_height: Some(response.proof_height.into()), + } + } +} + +/// Defines the RPC method response type when querying a list of packet commitments. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryPacketCommitmentsResponse { + pub commitments: Vec, + pub height: Height, + pub pagination: Option, +} + +impl QueryPacketCommitmentsResponse { + pub fn new( + commitments: Vec, + height: Height, + pagination: Option, + ) -> Self { + Self { + commitments, + height, + pagination, + } + } +} + +impl Protobuf for QueryPacketCommitmentsResponse {} + +impl TryFrom for QueryPacketCommitmentsResponse { + type Error = QueryError; + + fn try_from(value: RawQueryPacketCommitmentsResponse) -> Result { + Ok(Self { + commitments: value + .commitments + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + height: value + .height + .ok_or_else(|| QueryError::missing_field("height"))? + .try_into()?, + pagination: value.pagination.map(Into::into), + }) + } +} + +impl From for RawQueryPacketCommitmentsResponse { + fn from(response: QueryPacketCommitmentsResponse) -> Self { + Self { + commitments: response.commitments.into_iter().map(Into::into).collect(), + height: Some(response.height.into()), + pagination: response.pagination.map(Into::into), + } + } +} + +/// Defines the RPC method response type when querying a packet receipt. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryPacketReceiptResponse { + pub received: bool, + pub proof: Proof, + pub proof_height: Height, +} + +impl QueryPacketReceiptResponse { + pub fn new(received: bool, proof: Proof, proof_height: Height) -> Self { + Self { + received, + proof, + proof_height, + } + } +} + +impl Protobuf for QueryPacketReceiptResponse {} + +impl TryFrom for QueryPacketReceiptResponse { + type Error = QueryError; + + fn try_from(value: RawQueryPacketReceiptResponse) -> Result { + Ok(Self { + received: value.received, + proof: value.proof, + proof_height: value + .proof_height + .ok_or_else(|| QueryError::missing_field("proof_height"))? + .try_into()?, + }) + } +} + +impl From for RawQueryPacketReceiptResponse { + fn from(response: QueryPacketReceiptResponse) -> Self { + Self { + received: response.received, + proof: response.proof, + proof_height: Some(response.proof_height.into()), + } + } +} + +/// Defines the RPC method response type when querying a packet acknowledgement. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryPacketAcknowledgementResponse { + pub acknowledgement: AcknowledgementCommitment, + pub proof: Proof, + pub proof_height: Height, +} + +impl QueryPacketAcknowledgementResponse { + pub fn new( + acknowledgement: AcknowledgementCommitment, + proof: Proof, + proof_height: Height, + ) -> Self { + Self { + acknowledgement, + proof, + proof_height, + } + } +} + +impl Protobuf for QueryPacketAcknowledgementResponse {} + +impl TryFrom for QueryPacketAcknowledgementResponse { + type Error = QueryError; + + fn try_from(value: RawQueryPacketAcknowledgementResponse) -> Result { + Ok(Self { + acknowledgement: value.acknowledgement.into(), + proof: value.proof, + proof_height: value + .proof_height + .ok_or_else(|| QueryError::missing_field("proof_height"))? + .try_into()?, + }) + } +} + +impl From for RawQueryPacketAcknowledgementResponse { + fn from(response: QueryPacketAcknowledgementResponse) -> Self { + Self { + acknowledgement: response.acknowledgement.into_vec(), + proof: response.proof, + proof_height: Some(response.proof_height.into()), + } + } +} + +/// Defines the RPC method response type when querying a list of packet +/// acknowledgements. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryPacketAcknowledgementsResponse { + pub acknowledgements: Vec, + pub height: Height, + pub pagination: Option, +} + +impl QueryPacketAcknowledgementsResponse { + pub fn new( + acknowledgements: Vec, + height: Height, + pagination: Option, + ) -> Self { + Self { + acknowledgements, + height, + pagination, + } + } +} + +impl Protobuf for QueryPacketAcknowledgementsResponse {} + +impl TryFrom for QueryPacketAcknowledgementsResponse { + type Error = QueryError; + + fn try_from(value: RawQueryPacketAcknowledgementsResponse) -> Result { + Ok(Self { + acknowledgements: value + .acknowledgements + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + height: value + .height + .ok_or_else(|| QueryError::missing_field("height"))? + .try_into()?, + pagination: value.pagination.map(Into::into), + }) + } +} + +impl From for RawQueryPacketAcknowledgementsResponse { + fn from(response: QueryPacketAcknowledgementsResponse) -> Self { + Self { + acknowledgements: response + .acknowledgements + .into_iter() + .map(Into::into) + .collect(), + height: Some(response.height.into()), + pagination: response.pagination.map(Into::into), + } + } +} + +/// Defines the RPC method response type when querying a list of unreceived acks. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryUnreceivedAcksResponse { + pub sequences: Vec, + pub height: Height, +} + +impl QueryUnreceivedAcksResponse { + pub fn new(sequences: Vec, height: Height) -> Self { + Self { sequences, height } + } +} + +impl Protobuf for QueryUnreceivedAcksResponse {} + +impl TryFrom for QueryUnreceivedAcksResponse { + type Error = QueryError; + + fn try_from(value: RawQueryUnreceivedAcksResponse) -> Result { + Ok(Self { + sequences: value.sequences.into_iter().map(Into::into).collect(), + height: value + .height + .ok_or_else(|| QueryError::missing_field("height"))? + .try_into()?, + }) + } +} + +impl From for RawQueryUnreceivedAcksResponse { + fn from(response: QueryUnreceivedAcksResponse) -> Self { + Self { + sequences: response.sequences.into_iter().map(Into::into).collect(), + height: Some(response.height.into()), + } + } +} + +/// Defines the RPC method response type when querying a list of unreceived packets. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryUnreceivedPacketsResponse { + pub sequences: Vec, + pub height: Height, +} + +impl QueryUnreceivedPacketsResponse { + pub fn new(sequences: Vec, height: Height) -> Self { + Self { sequences, height } + } +} + +impl Protobuf for QueryUnreceivedPacketsResponse {} + +impl TryFrom for QueryUnreceivedPacketsResponse { + type Error = QueryError; + + fn try_from(value: RawQueryUnreceivedPacketsResponse) -> Result { + Ok(Self { + sequences: value.sequences.into_iter().map(Into::into).collect(), + height: value + .height + .ok_or_else(|| QueryError::missing_field("height"))? + .try_into()?, + }) + } +} + +impl From for RawQueryUnreceivedPacketsResponse { + fn from(response: QueryUnreceivedPacketsResponse) -> Self { + Self { + sequences: response.sequences.into_iter().map(Into::into).collect(), + height: Some(response.height.into()), + } + } +} + +/// Defines the RPC method response type when querying the next sequence to be received on a channel. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryNextSequenceReceiveResponse { + pub next_sequence_receive: Sequence, + pub proof: Proof, + pub proof_height: Height, +} + +impl QueryNextSequenceReceiveResponse { + pub fn new(next_sequence_receive: Sequence, proof: Proof, proof_height: Height) -> Self { + Self { + next_sequence_receive, + proof, + proof_height, + } + } +} + +impl Protobuf for QueryNextSequenceReceiveResponse {} + +impl TryFrom for QueryNextSequenceReceiveResponse { + type Error = QueryError; + + fn try_from(value: RawQueryNextSequenceReceiveResponse) -> Result { + Ok(Self { + next_sequence_receive: value.next_sequence_receive.into(), + proof: value.proof, + proof_height: value + .proof_height + .ok_or_else(|| QueryError::missing_field("proof_height"))? + .try_into()?, + }) + } +} + +impl From for RawQueryNextSequenceReceiveResponse { + fn from(response: QueryNextSequenceReceiveResponse) -> Self { + Self { + next_sequence_receive: response.next_sequence_receive.into(), + proof: response.proof, + proof_height: Some(response.proof_height.into()), + } + } +} + +/// Defines the RPC method response type when querying the next sequence to be +/// sent on a channel. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryNextSequenceSendResponse { + pub next_sequence_send: Sequence, + pub proof: Proof, + pub proof_height: Height, +} + +impl QueryNextSequenceSendResponse { + pub fn new(next_sequence_send: Sequence, proof: Proof, proof_height: Height) -> Self { + Self { + next_sequence_send, + proof, + proof_height, + } + } +} + +impl Protobuf for QueryNextSequenceSendResponse {} + +impl TryFrom for QueryNextSequenceSendResponse { + type Error = QueryError; + + fn try_from(value: RawQueryNextSequenceSendResponse) -> Result { + Ok(Self { + next_sequence_send: value.next_sequence_send.into(), + proof: value.proof, + proof_height: value + .proof_height + .ok_or_else(|| QueryError::missing_field("proof_height"))? + .try_into()?, + }) + } +} + +impl From for RawQueryNextSequenceSendResponse { + fn from(response: QueryNextSequenceSendResponse) -> Self { + Self { + next_sequence_send: response.next_sequence_send.into(), + proof: response.proof, + proof_height: Some(response.proof_height.into()), + } + } +} diff --git a/ibc-query/src/core/client/mod.rs b/ibc-query/src/core/client/mod.rs index b24e36180..7cdb244ce 100644 --- a/ibc-query/src/core/client/mod.rs +++ b/ibc-query/src/core/client/mod.rs @@ -1,5 +1,7 @@ mod query; mod service; +mod types; pub use query::*; pub use service::*; +pub use types::*; diff --git a/ibc-query/src/core/client/query.rs b/ibc-query/src/core/client/query.rs index e92d320af..52ce7d5b1 100644 --- a/ibc-query/src/core/client/query.rs +++ b/ibc-query/src/core/client/query.rs @@ -1,29 +1,26 @@ //! Provides utility functions for querying IBC client states. -use alloc::format; -use core::str::FromStr; - use ibc::core::client::context::client_state::ClientStateValidation; use ibc::core::client::context::ClientValidationContext; use ibc::core::client::types::error::ClientError; -use ibc::core::client::types::Height; -use ibc::core::host::types::identifiers::ClientId; use ibc::core::host::types::path::{ ClientConsensusStatePath, ClientStatePath, Path, UpgradeClientPath, }; use ibc::core::host::{ConsensusStateRef, ValidationContext}; use ibc::cosmos_host::upgrade_proposal::{UpgradeValidationContext, UpgradedConsensusStateRef}; -use ibc_proto::google::protobuf::Any; -use ibc_proto::ibc::core::client::v1::{ - ConsensusStateWithHeight, IdentifiedClientState, QueryClientStateRequest, - QueryClientStateResponse, QueryClientStatesRequest, QueryClientStatesResponse, - QueryClientStatusRequest, QueryClientStatusResponse, QueryConsensusStateHeightsRequest, +use ibc::primitives::prelude::format; +use ibc::primitives::proto::Any; + +use super::{ + ConsensusStateWithHeight, IdentifiedClientState, QueryClientStateResponse, + QueryClientStatesRequest, QueryClientStatesResponse, QueryClientStatusRequest, + QueryClientStatusResponse, QueryConsensusStateHeightsRequest, QueryConsensusStateHeightsResponse, QueryConsensusStateRequest, QueryConsensusStateResponse, QueryConsensusStatesRequest, QueryConsensusStatesResponse, QueryUpgradedClientStateRequest, QueryUpgradedClientStateResponse, QueryUpgradedConsensusStateRequest, QueryUpgradedConsensusStateResponse, }; - +use crate::core::client::QueryClientStateRequest; use crate::core::context::QueryContext; use crate::error::QueryError; @@ -35,7 +32,7 @@ pub fn query_client_state( where I: QueryContext, { - let client_id = ClientId::from_str(request.client_id.as_str())?; + let client_id = request.client_id.clone(); let client_val_ctx = ibc_ctx.get_client_validation_context(); @@ -48,15 +45,17 @@ where current_height, &Path::ClientState(ClientStatePath::new(client_id.clone())), ) - .ok_or(QueryError::ProofNotFound { - description: format!("Proof not found for client state path: {client_id:?}"), + .ok_or_else(|| { + QueryError::proof_not_found(format!( + "Proof not found for client state path: {client_id:?}" + )) })?; - Ok(QueryClientStateResponse { - client_state: Some(client_state.into()), + Ok(QueryClientStateResponse::new( + client_state.into(), proof, - proof_height: Some(current_height.into()), - }) + current_height, + )) } /// Queries for all the existing client states. @@ -69,17 +68,14 @@ where { let client_states = ibc_ctx.client_states()?; - Ok(QueryClientStatesResponse { - client_states: client_states + Ok(QueryClientStatesResponse::new( + client_states .into_iter() - .map(|(id, state)| IdentifiedClientState { - client_id: id.into(), - client_state: Some(state.into()), - }) + .map(|(id, state)| IdentifiedClientState::new(id, state.into())) .collect(), // no support for pagination yet - pagination: None, - }) + None, + )) } /// Queries for the consensus state of a given client id and height. @@ -91,19 +87,9 @@ where I: QueryContext, ConsensusStateRef: 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 client_id = request.client_id.clone(); + let (height, consensus_state) = if let Some(height) = request.consensus_height { let client_val_ctx = ibc_ctx.get_client_validation_context(); let consensus_state = client_val_ctx.consensus_state(&ClientConsensusStatePath::new( @@ -113,6 +99,16 @@ where ))?; (height, consensus_state) + } else { + ibc_ctx + .consensus_states(&client_id)? + .into_iter() + .max_by_key(|&(h, _)| h) + .ok_or_else(|| { + QueryError::proof_not_found(format!( + "No consensus state found for client: {client_id:?}" + )) + })? }; let current_height = ibc_ctx.host_height()?; @@ -126,15 +122,17 @@ where height.revision_height(), )), ) - .ok_or(QueryError::ProofNotFound { - description: format!("Proof not found for consensus state path: {client_id:?}"), + .ok_or_else(|| { + QueryError::proof_not_found(format!( + "Proof not found for consensus state path: {client_id:?}" + )) })?; - Ok(QueryConsensusStateResponse { - consensus_state: Some(consensus_state.into()), + Ok(QueryConsensusStateResponse::new( + consensus_state.into(), proof, - proof_height: Some(current_height.into()), - }) + current_height, + )) } /// Queries for all the consensus states of a given client id. @@ -146,21 +144,16 @@ where I: QueryContext, ConsensusStateRef: Into, { - let client_id = ClientId::from_str(request.client_id.as_str())?; - - let consensus_states = ibc_ctx.consensus_states(&client_id)?; + let consensus_states = ibc_ctx.consensus_states(&request.client_id)?; - Ok(QueryConsensusStatesResponse { - consensus_states: consensus_states + Ok(QueryConsensusStatesResponse::new( + consensus_states .into_iter() - .map(|(height, state)| ConsensusStateWithHeight { - height: Some(height.into()), - consensus_state: Some(state.into()), - }) + .map(|(height, state)| ConsensusStateWithHeight::new(height, state.into())) .collect(), - // no support for pagination yet - pagination: None, - }) + // no support for pagination yet, + None, + )) } /// Queries for the heights of all the consensus states of a given client id. @@ -171,18 +164,13 @@ pub fn query_consensus_state_heights( 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)?; + let consensus_state_heights = ibc_ctx.consensus_state_heights(&request.client_id)?; - Ok(QueryConsensusStateHeightsResponse { - consensus_state_heights: consensus_state_heights - .into_iter() - .map(Into::into) - .collect(), + Ok(QueryConsensusStateHeightsResponse::new( + consensus_state_heights, // no support for pagination yet - pagination: None, - }) + None, + )) } /// Queries for the status (Active, Frozen, Expired, Unauthorized) of a given client. @@ -193,15 +181,12 @@ pub fn query_client_status( where I: ValidationContext, { - let client_id = ClientId::from_str(request.client_id.as_str())?; let client_val_ctx = ibc_ctx.get_client_validation_context(); - let client_state = client_val_ctx.client_state(&client_id)?; + let client_state = client_val_ctx.client_state(&request.client_id)?; let client_validation_ctx = ibc_ctx.get_client_validation_context(); - let client_status = client_state.status(client_validation_ctx, &client_id)?; + let client_status = client_state.status(client_validation_ctx, &request.client_id)?; - Ok(QueryClientStatusResponse { - status: format!("{client_status}"), - }) + Ok(QueryClientStatusResponse::new(client_status)) } /// Queries for the upgraded client state. @@ -220,9 +205,9 @@ where .upgraded_client_state(&upgraded_client_state_path) .map_err(ClientError::from)?; - Ok(QueryUpgradedClientStateResponse { - upgraded_client_state: Some(upgraded_client_state.into()), - }) + Ok(QueryUpgradedClientStateResponse::new( + upgraded_client_state.into(), + )) } /// Queries for the upgraded consensus state. @@ -243,7 +228,7 @@ where .upgraded_consensus_state(&upgraded_consensus_state_path) .map_err(ClientError::from)?; - Ok(QueryUpgradedConsensusStateResponse { - upgraded_consensus_state: Some(upgraded_consensus_state.into()), - }) + Ok(QueryUpgradedConsensusStateResponse::new( + upgraded_consensus_state.into(), + )) } diff --git a/ibc-query/src/core/client/service.rs b/ibc-query/src/core/client/service.rs index a1a7d7d32..b5bd21d1e 100644 --- a/ibc-query/src/core/client/service.rs +++ b/ibc-query/src/core/client/service.rs @@ -26,6 +26,7 @@ use super::{ query_upgraded_consensus_state, }; use crate::core::context::QueryContext; +use crate::utils::{IntoDomain, IntoResponse, TryIntoDomain}; // TODO(rano): currently the services don't support pagination, so we return all the results. @@ -71,54 +72,43 @@ where &self, request: Request, ) -> Result, Status> { - let response = query_client_state(&self.ibc_context, request.get_ref())?; - - Ok(Response::new(response)) + query_client_state(&self.ibc_context, &request.try_into_domain()?)?.into_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)) + query_client_states(&self.ibc_context, &request.into_domain())?.into_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)) + query_consensus_state(&self.ibc_context, &request.try_into_domain()?)?.into_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)) + query_consensus_states(&self.ibc_context, &request.try_into_domain()?)?.into_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)) + query_consensus_state_heights(&self.ibc_context, &request.try_into_domain()?)? + .into_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)) + query_client_status(&self.ibc_context, &request.try_into_domain()?)?.into_response() } async fn client_params( @@ -134,17 +124,14 @@ where &self, request: Request, ) -> Result, Status> { - let response = query_upgraded_client_state(&self.upgrade_context, request.get_ref())?; - - Ok(Response::new(response)) + query_upgraded_client_state(&self.upgrade_context, &request.into_domain())?.into_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)) + query_upgraded_consensus_state(&self.upgrade_context, &request.into_domain())? + .into_response() } } diff --git a/ibc-query/src/core/client/types/mod.rs b/ibc-query/src/core/client/types/mod.rs new file mode 100644 index 000000000..b8be6322b --- /dev/null +++ b/ibc-query/src/core/client/types/mod.rs @@ -0,0 +1,5 @@ +mod request; +mod response; + +pub use request::*; +pub use response::*; diff --git a/ibc-query/src/core/client/types/request.rs b/ibc-query/src/core/client/types/request.rs new file mode 100644 index 000000000..ffb9d6170 --- /dev/null +++ b/ibc-query/src/core/client/types/request.rs @@ -0,0 +1,229 @@ +//! Contains all the RPC method request domain types and their conversions to +//! and from the corresponding gRPC proto types for the client module. + +use ibc::core::client::types::Height; +use ibc::core::host::types::identifiers::ClientId; +use ibc::primitives::prelude::*; +use ibc_proto::ibc::core::client::v1::{ + QueryClientParamsRequest as RawQueryClientParamsRequest, + QueryClientStateRequest as RawQueryClientStateRequest, + QueryClientStatesRequest as RawQueryClientStatesRequest, + QueryClientStatusRequest as RawQueryClientStatusRequest, + QueryConsensusStateHeightsRequest as RawQueryConsensusStateHeightsRequest, + QueryConsensusStateRequest as RawQueryConsensusStateRequest, + QueryConsensusStatesRequest as RawQueryConsensusStatesRequest, + QueryUpgradedClientStateRequest as RawUpgradedClientStateRequest, + QueryUpgradedConsensusStateRequest as RawUpgradedConsensusStateRequest, +}; +use ibc_proto::Protobuf; + +use crate::error::QueryError; +use crate::types::PageRequest; + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryClientStateRequest { + /// The client identifier. + pub client_id: ClientId, + /// The height at which to query the client state. If not provided, the + /// latest height should be used. + pub query_height: Option, +} + +impl TryFrom for QueryClientStateRequest { + type Error = QueryError; + + fn try_from(request: RawQueryClientStateRequest) -> Result { + Ok(Self { + client_id: request.client_id.parse()?, + query_height: None, + }) + } +} + +/// Defines the RPC method request type for querying all client states. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryClientStatesRequest { + pub pagination: Option, +} + +impl From for QueryClientStatesRequest { + fn from(request: RawQueryClientStatesRequest) -> Self { + Self { + pagination: request.pagination.map(Into::into), + } + } +} + +/// Defines the RPC method request type for querying the consensus state of a +/// client. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConsensusStateRequest { + /// The client identifier. + pub client_id: ClientId, + /// The consensus state height to be queried. If not provided, the latest + /// height + pub consensus_height: Option, + /// The height at which to query the consensus state. If not provided, the + /// latest height should be used. + pub query_height: Option, +} + +impl TryFrom for QueryConsensusStateRequest { + type Error = QueryError; + + fn try_from(request: RawQueryConsensusStateRequest) -> Result { + Ok(Self { + client_id: request.client_id.parse()?, + consensus_height: (!request.latest_height) + .then(|| Height::new(request.revision_number, request.revision_height)) + .transpose()?, + query_height: None, + }) + } +} + +/// Defines the RPC method request type for querying all consensus states. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConsensusStatesRequest { + pub client_id: ClientId, + pub pagination: Option, +} + +impl Protobuf for QueryConsensusStatesRequest {} + +impl TryFrom for QueryConsensusStatesRequest { + type Error = QueryError; + + fn try_from(request: RawQueryConsensusStatesRequest) -> Result { + Ok(Self { + client_id: request.client_id.parse()?, + pagination: request.pagination.map(Into::into), + }) + } +} + +impl From for RawQueryConsensusStatesRequest { + fn from(request: QueryConsensusStatesRequest) -> Self { + Self { + client_id: request.client_id.to_string(), + pagination: request.pagination.map(Into::into), + } + } +} + +/// Defines the RPC method request type for querying the consensus state +/// heights. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConsensusStateHeightsRequest { + pub client_id: ClientId, + pub pagination: Option, +} + +impl Protobuf for QueryConsensusStateHeightsRequest {} + +impl TryFrom for QueryConsensusStateHeightsRequest { + type Error = QueryError; + + fn try_from(request: RawQueryConsensusStateHeightsRequest) -> Result { + Ok(Self { + client_id: request.client_id.parse()?, + pagination: request.pagination.map(Into::into), + }) + } +} + +impl From for RawQueryConsensusStateHeightsRequest { + fn from(request: QueryConsensusStateHeightsRequest) -> Self { + Self { + client_id: request.client_id.to_string(), + pagination: request.pagination.map(Into::into), + } + } +} + +/// Defines the RPC method request type for querying the host consensus state. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryHostConsensusStateRequest { + pub query_height: Option, +} + +/// Defines the RPC method request type for querying the status of a client. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryClientStatusRequest { + pub client_id: ClientId, + pub query_height: Option, +} + +impl TryFrom for QueryClientStatusRequest { + type Error = QueryError; + + fn try_from(request: RawQueryClientStatusRequest) -> Result { + Ok(Self { + client_id: request.client_id.parse()?, + query_height: None, + }) + } +} + +/// Defines the RPC method request type for querying the parameters of a client. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryClientParamsRequest { + pub query_height: Option, +} + +impl From for QueryClientParamsRequest { + fn from(_request: RawQueryClientParamsRequest) -> Self { + Self { query_height: None } + } +} + +/// Defines the RPC method request type for querying the upgraded client state. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryUpgradedClientStateRequest { + /// Height at which the chain is scheduled to halt for upgrade + pub upgrade_height: Option, +} + +impl From for QueryUpgradedClientStateRequest { + fn from(_request: RawUpgradedClientStateRequest) -> Self { + Self { + upgrade_height: None, + } + } +} + +/// Defines the RPC method request type for querying the upgraded consensus +/// state. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryUpgradedConsensusStateRequest { + /// Height at which the chain is scheduled to halt for upgrade. + pub upgrade_height: Option, +} + +impl From for QueryUpgradedConsensusStateRequest { + fn from(_request: RawUpgradedConsensusStateRequest) -> Self { + Self { + upgrade_height: None, + } + } +} diff --git a/ibc-query/src/core/client/types/response.rs b/ibc-query/src/core/client/types/response.rs new file mode 100644 index 000000000..587fe2127 --- /dev/null +++ b/ibc-query/src/core/client/types/response.rs @@ -0,0 +1,508 @@ +//! Contains all the RPC method response domain types and their conversions to +//! and from the corresponding gRPC proto types for the client module. + +use ibc::core::client::types::{Height, Status}; +use ibc::core::host::types::identifiers::ClientId; +use ibc::core::primitives::proto::Any; +use ibc::primitives::prelude::*; +use ibc::primitives::proto::Protobuf; +use ibc_proto::ibc::core::client::v1::{ + ConsensusStateWithHeight as RawConsensusStateWithHeight, + IdentifiedClientState as RawIdentifiedClientState, Params as RawParams, + QueryClientParamsResponse as RawQueryClientParamsResponse, + QueryClientStateResponse as RawQueryClientStateResponse, + QueryClientStatesResponse as RawQueryClientStatesResponse, + QueryClientStatusResponse as RawQueryClientStatusResponse, + QueryConsensusStateHeightsResponse as RawQueryConsensusStateHeightsResponse, + QueryConsensusStateResponse as RawQueryConsensusStateResponse, + QueryConsensusStatesResponse as RawQueryConsensusStatesResponse, + QueryUpgradedClientStateResponse as RawQueryUpgradedClientStateResponse, + QueryUpgradedConsensusStateResponse as RawQueryUpgradedConsensusStateResponse, +}; + +use crate::error::QueryError; +use crate::types::{PageResponse, Proof}; + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryClientStateResponse { + /// The client identifier. + pub client_state: Any, + /// The proof that the client state was retrieved. + pub proof: Proof, + /// The height at which the client state was retrieved. + pub proof_height: Height, +} + +impl QueryClientStateResponse { + pub fn new(client_state: Any, proof: Proof, proof_height: Height) -> Self { + Self { + client_state, + proof, + proof_height, + } + } +} + +impl Protobuf for QueryClientStateResponse {} + +impl TryFrom for QueryClientStateResponse { + type Error = QueryError; + + fn try_from(value: RawQueryClientStateResponse) -> Result { + Ok(Self { + client_state: value + .client_state + .ok_or_else(|| QueryError::missing_field("client_state"))?, + proof: value.proof, + proof_height: value + .proof_height + .ok_or_else(|| QueryError::missing_field("proof_height"))? + .try_into()?, + }) + } +} + +impl From for RawQueryClientStateResponse { + fn from(response: QueryClientStateResponse) -> Self { + Self { + client_state: Some(response.client_state), + proof: response.proof, + proof_height: Some(response.proof_height.into()), + } + } +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryClientStatesResponse { + pub client_states: Vec, + pub pagination: Option, +} + +impl QueryClientStatesResponse { + pub fn new( + client_states: Vec, + pagination: Option, + ) -> Self { + Self { + client_states, + pagination, + } + } +} + +impl Protobuf for QueryClientStatesResponse {} + +impl TryFrom for QueryClientStatesResponse { + type Error = QueryError; + + fn try_from(value: RawQueryClientStatesResponse) -> Result { + Ok(Self { + client_states: value + .client_states + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + pagination: value.pagination.map(Into::into), + }) + } +} + +impl From for RawQueryClientStatesResponse { + fn from(response: QueryClientStatesResponse) -> Self { + Self { + client_states: response.client_states.into_iter().map(Into::into).collect(), + pagination: response.pagination.map(Into::into), + } + } +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct IdentifiedClientState { + pub client_id: ClientId, + pub client_state: Any, +} + +impl IdentifiedClientState { + pub fn new(client_id: ClientId, client_state: Any) -> Self { + Self { + client_id, + client_state, + } + } +} + +impl Protobuf for IdentifiedClientState {} + +impl TryFrom for IdentifiedClientState { + type Error = QueryError; + + fn try_from(value: RawIdentifiedClientState) -> Result { + Ok(Self { + client_id: value.client_id.parse()?, + client_state: value + .client_state + .ok_or_else(|| QueryError::missing_field("client_state"))?, + }) + } +} + +impl From for RawIdentifiedClientState { + fn from(ics: IdentifiedClientState) -> Self { + Self { + client_id: ics.client_id.to_string(), + client_state: Some(ics.client_state), + } + } +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConsensusStateResponse { + consensus_state: Any, + proof: Proof, + proof_height: Height, +} + +impl QueryConsensusStateResponse { + pub fn new(consensus_state: Any, proof: Proof, proof_height: Height) -> Self { + Self { + consensus_state, + proof, + proof_height, + } + } +} + +impl Protobuf for QueryConsensusStateResponse {} + +impl TryFrom for QueryConsensusStateResponse { + type Error = QueryError; + + fn try_from(value: RawQueryConsensusStateResponse) -> Result { + Ok(Self { + consensus_state: value + .consensus_state + .ok_or_else(|| QueryError::missing_field("consensus_state"))?, + proof: value.proof, + proof_height: value + .proof_height + .ok_or_else(|| QueryError::missing_field("proof_height"))? + .try_into()?, + }) + } +} + +impl From for RawQueryConsensusStateResponse { + fn from(response: QueryConsensusStateResponse) -> Self { + Self { + consensus_state: Some(response.consensus_state), + proof: response.proof, + proof_height: Some(response.proof_height.into()), + } + } +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct ConsensusStateWithHeight { + height: Height, + consensus_state: Any, +} + +impl ConsensusStateWithHeight { + pub fn new(height: Height, consensus_state: Any) -> Self { + Self { + height, + consensus_state, + } + } +} + +impl Protobuf for ConsensusStateWithHeight {} + +impl TryFrom for ConsensusStateWithHeight { + type Error = QueryError; + + fn try_from(value: RawConsensusStateWithHeight) -> Result { + Ok(Self { + height: value + .height + .ok_or_else(|| QueryError::missing_field("height"))? + .try_into()?, + consensus_state: value + .consensus_state + .ok_or_else(|| QueryError::missing_field("consensus_state"))?, + }) + } +} + +impl From for RawConsensusStateWithHeight { + fn from(ics: ConsensusStateWithHeight) -> Self { + Self { + height: Some(ics.height.into()), + consensus_state: Some(ics.consensus_state), + } + } +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConsensusStatesResponse { + consensus_states: Vec, + pagination: Option, +} + +impl QueryConsensusStatesResponse { + pub fn new( + consensus_states: Vec, + pagination: Option, + ) -> Self { + Self { + consensus_states, + pagination, + } + } +} + +impl Protobuf for QueryConsensusStatesResponse {} + +impl TryFrom for QueryConsensusStatesResponse { + type Error = QueryError; + + fn try_from(value: RawQueryConsensusStatesResponse) -> Result { + Ok(Self { + consensus_states: value + .consensus_states + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + pagination: value.pagination.map(Into::into), + }) + } +} + +impl From for RawQueryConsensusStatesResponse { + fn from(response: QueryConsensusStatesResponse) -> Self { + Self { + consensus_states: response + .consensus_states + .into_iter() + .map(Into::into) + .collect(), + pagination: response.pagination.map(Into::into), + } + } +} + +/// Defines the RPC method response type for querying the consensus state heights. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConsensusStateHeightsResponse { + consensus_state_heights: Vec, + pagination: Option, +} + +impl QueryConsensusStateHeightsResponse { + pub fn new(consensus_state_heights: Vec, pagination: Option) -> Self { + Self { + consensus_state_heights, + pagination, + } + } +} + +impl Protobuf for QueryConsensusStateHeightsResponse {} + +impl TryFrom for QueryConsensusStateHeightsResponse { + type Error = QueryError; + + fn try_from(value: RawQueryConsensusStateHeightsResponse) -> Result { + Ok(Self { + consensus_state_heights: value + .consensus_state_heights + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + pagination: value.pagination.map(Into::into), + }) + } +} + +impl From for RawQueryConsensusStateHeightsResponse { + fn from(response: QueryConsensusStateHeightsResponse) -> Self { + Self { + consensus_state_heights: response + .consensus_state_heights + .into_iter() + .map(Into::into) + .collect(), + pagination: response.pagination.map(Into::into), + } + } +} + +/// Defines the RPC method response type for querying the client status. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryClientStatusResponse { + status: Status, +} + +impl QueryClientStatusResponse { + pub fn new(status: Status) -> Self { + Self { status } + } +} + +impl Protobuf for QueryClientStatusResponse {} + +impl TryFrom for QueryClientStatusResponse { + type Error = QueryError; + + fn try_from(value: RawQueryClientStatusResponse) -> Result { + Ok(Self { + status: value.status.parse()?, + }) + } +} + +impl From for RawQueryClientStatusResponse { + fn from(response: QueryClientStatusResponse) -> Self { + Self { + status: response.status.to_string(), + } + } +} + +/// Defines the RPC method response type for querying the client parameters. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryClientParamsResponse { + allowed_clients: Vec, +} + +impl QueryClientParamsResponse { + pub fn new(allowed_clients: Vec) -> Self { + Self { allowed_clients } + } +} + +impl Protobuf for QueryClientParamsResponse {} + +impl TryFrom for QueryClientParamsResponse { + type Error = QueryError; + + fn try_from(value: RawQueryClientParamsResponse) -> Result { + Ok(Self { + allowed_clients: value + .params + .ok_or_else(|| QueryError::missing_field("params"))? + .allowed_clients + .into_iter() + .map(|id| id.parse()) + .collect::>()?, + }) + } +} + +impl From for RawQueryClientParamsResponse { + fn from(response: QueryClientParamsResponse) -> Self { + Self { + params: Some(RawParams { + allowed_clients: response + .allowed_clients + .into_iter() + .map(|id| id.to_string()) + .collect(), + }), + } + } +} + +/// Defines the RPC method response type for querying the upgraded client state. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryUpgradedClientStateResponse { + upgraded_client_state: Any, +} + +impl QueryUpgradedClientStateResponse { + pub fn new(upgraded_client_state: Any) -> Self { + Self { + upgraded_client_state, + } + } +} + +impl Protobuf for QueryUpgradedClientStateResponse {} + +impl TryFrom for QueryUpgradedClientStateResponse { + type Error = QueryError; + + fn try_from(value: RawQueryUpgradedClientStateResponse) -> Result { + Ok(Self { + upgraded_client_state: value + .upgraded_client_state + .ok_or_else(|| QueryError::missing_field("upgraded_client_state"))?, + }) + } +} + +impl From for RawQueryUpgradedClientStateResponse { + fn from(response: QueryUpgradedClientStateResponse) -> Self { + Self { + upgraded_client_state: Some(response.upgraded_client_state), + } + } +} + +/// Defines the RPC method response type for querying the upgraded consensus state. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryUpgradedConsensusStateResponse { + upgraded_consensus_state: Any, +} + +impl QueryUpgradedConsensusStateResponse { + pub fn new(upgraded_consensus_state: Any) -> Self { + Self { + upgraded_consensus_state, + } + } +} + +impl Protobuf for QueryUpgradedConsensusStateResponse {} + +impl TryFrom for QueryUpgradedConsensusStateResponse { + type Error = QueryError; + + fn try_from(value: RawQueryUpgradedConsensusStateResponse) -> Result { + Ok(Self { + upgraded_consensus_state: value + .upgraded_consensus_state + .ok_or_else(|| QueryError::missing_field("upgraded_consensus_state"))?, + }) + } +} + +impl From for RawQueryUpgradedConsensusStateResponse { + fn from(response: QueryUpgradedConsensusStateResponse) -> Self { + Self { + upgraded_consensus_state: Some(response.upgraded_consensus_state), + } + } +} diff --git a/ibc-query/src/core/connection/mod.rs b/ibc-query/src/core/connection/mod.rs index b24e36180..7cdb244ce 100644 --- a/ibc-query/src/core/connection/mod.rs +++ b/ibc-query/src/core/connection/mod.rs @@ -1,5 +1,7 @@ mod query; mod service; +mod types; pub use query::*; pub use service::*; +pub use types::*; diff --git a/ibc-query/src/core/connection/query.rs b/ibc-query/src/core/connection/query.rs index 09d304806..419c45417 100644 --- a/ibc-query/src/core/connection/query.rs +++ b/ibc-query/src/core/connection/query.rs @@ -1,28 +1,24 @@ //! Provides utility functions for querying IBC connection states. -use alloc::format; -use alloc::vec::Vec; -use core::str::FromStr; - use ibc::core::client::context::ClientValidationContext; -use ibc::core::client::types::Height; -use ibc::core::host::types::identifiers::{ClientId, ConnectionId}; use ibc::core::host::types::path::{ ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, ConnectionPath, Path, }; use ibc::core::host::{ConsensusStateRef, ValidationContext}; -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, +use ibc::primitives::prelude::format; +use ibc::primitives::proto::Any; + +use super::{ + QueryClientConnectionsRequest, QueryClientConnectionsResponse, QueryConnectionClientStateRequest, QueryConnectionClientStateResponse, QueryConnectionConsensusStateRequest, QueryConnectionConsensusStateResponse, QueryConnectionParamsRequest, QueryConnectionParamsResponse, QueryConnectionRequest, QueryConnectionResponse, QueryConnectionsRequest, QueryConnectionsResponse, }; - +use crate::core::client::IdentifiedClientState; use crate::core::context::{ProvableContext, QueryContext}; use crate::error::QueryError; +use crate::types::Proof; /// Queries for the connection end of a given connection id. pub fn query_connection( @@ -32,26 +28,27 @@ pub fn query_connection( 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 connection_end = ibc_ctx.connection_end(&request.connection_id)?; let current_height = ibc_ctx.host_height()?; let proof = ibc_ctx .get_proof( current_height, - &Path::Connection(ConnectionPath::new(&connection_id)), + &Path::Connection(ConnectionPath::new(&request.connection_id)), ) - .ok_or(QueryError::ProofNotFound { - description: format!("Proof not found for connection path: {connection_id:?}"), + .ok_or_else(|| { + QueryError::proof_not_found(format!( + "Proof not found for connection path: {:?}", + request.connection_id + )) })?; - Ok(QueryConnectionResponse { - connection: Some(connection_end.into()), + Ok(QueryConnectionResponse::new( + connection_end, proof, - proof_height: Some(current_height.into()), - }) + current_height, + )) } /// Queries for all the existing connection ends. @@ -64,12 +61,11 @@ where { 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, - }) + Ok(QueryConnectionsResponse::new( + connections, + ibc_ctx.host_height()?, + None, + )) } /// Queries for all the existing connection ends for a given client. @@ -80,26 +76,27 @@ pub fn query_client_connections( where I: QueryContext, { - let client_id = ClientId::from_str(request.client_id.as_str())?; - - let connections = ibc_ctx.client_connection_ends(&client_id)?; + let connections = ibc_ctx.client_connection_ends(&request.client_id)?; let current_height = ibc_ctx.host_height()?; - let proof: Vec = ibc_ctx + let proof: Proof = ibc_ctx .get_proof( current_height, - &Path::ClientConnection(ClientConnectionPath::new(client_id.clone())), + &Path::ClientConnection(ClientConnectionPath::new(request.client_id.clone())), ) - .ok_or(QueryError::ProofNotFound { - description: format!("Proof not found for client connection path: {client_id:?}"), + .ok_or_else(|| { + QueryError::proof_not_found(format!( + "Proof not found for client connection path: {:?}", + request.client_id + )) })?; - Ok(QueryClientConnectionsResponse { - connection_paths: connections.into_iter().map(|x| x.as_str().into()).collect(), + Ok(QueryClientConnectionsResponse::new( + connections, proof, - proof_height: Some(current_height.into()), - }) + current_height, + )) } /// Queries for the client state of a given connection id. @@ -110,9 +107,7 @@ pub fn query_connection_client_state( where I: QueryContext, { - let connection_id = ConnectionId::from_str(request.connection_id.as_str())?; - - let connection_end = ibc_ctx.connection_end(&connection_id)?; + let connection_end = ibc_ctx.connection_end(&request.connection_id)?; let client_val_ctx = ibc_ctx.get_client_validation_context(); @@ -125,21 +120,18 @@ where current_height, &Path::ClientState(ClientStatePath::new(connection_end.client_id().clone())), ) - .ok_or(QueryError::ProofNotFound { - description: format!( + .ok_or_else(|| { + QueryError::proof_not_found(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()), - }), + Ok(QueryConnectionClientStateResponse::new( + IdentifiedClientState::new(connection_end.client_id().clone(), client_state.into()), proof, - proof_height: Some(current_height.into()), - }) + current_height, + )) } /// Queries for the consensus state of a given connection id and height. @@ -151,16 +143,12 @@ where I: ValidationContext + ProvableContext, ConsensusStateRef: 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 connection_end = ibc_ctx.connection_end(&request.connection_id)?; let consensus_path = ClientConsensusStatePath::new( connection_end.client_id().clone(), - height.revision_number(), - height.revision_height(), + request.height.revision_number(), + request.height.revision_height(), ); let client_val_ctx = ibc_ctx.get_client_validation_context(); @@ -171,19 +159,19 @@ where let proof = ibc_ctx .get_proof(current_height, &Path::ClientConsensusState(consensus_path)) - .ok_or(QueryError::ProofNotFound { - description: format!( + .ok_or_else(|| { + QueryError::proof_not_found(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(), + Ok(QueryConnectionConsensusStateResponse::new( + consensus_state.into(), + connection_end.client_id().clone(), proof, - proof_height: Some(current_height.into()), - }) + current_height, + )) } /// Queries for the connection parameters. @@ -194,9 +182,7 @@ pub fn query_connection_params( where I: QueryContext, { - Ok(QueryConnectionParamsResponse { - params: Some(ConnectionParams { - max_expected_time_per_block: ibc_ctx.max_expected_time_per_block().as_secs(), - }), - }) + Ok(QueryConnectionParamsResponse::new( + ibc_ctx.max_expected_time_per_block().as_secs(), + )) } diff --git a/ibc-query/src/core/connection/service.rs b/ibc-query/src/core/connection/service.rs index 75c20b185..3828f071e 100644 --- a/ibc-query/src/core/connection/service.rs +++ b/ibc-query/src/core/connection/service.rs @@ -1,6 +1,7 @@ -//! [`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. +//! [`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 ibc::core::host::ConsensusStateRef; use ibc::core::primitives::prelude::*; @@ -20,11 +21,13 @@ use super::{ query_connection_consensus_state, query_connection_params, query_connections, }; use crate::core::context::QueryContext; +use crate::utils::{IntoDomain, IntoResponse, TryIntoDomain}; // 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. +/// 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, @@ -38,8 +41,9 @@ where I: QueryContext + Send + Sync + 'static, ConsensusStateRef: 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. + /// 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 } } @@ -55,53 +59,43 @@ where &self, request: Request, ) -> Result, Status> { - let response = query_connection(&self.ibc_context, request.get_ref())?; - - Ok(Response::new(response)) + query_connection(&self.ibc_context, &request.try_into_domain()?)?.into_response() } async fn connections( &self, request: Request, ) -> Result, Status> { - let response = query_connections(&self.ibc_context, request.get_ref())?; - - Ok(Response::new(response)) + query_connections(&self.ibc_context, &request.into_domain())?.into_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)) + query_client_connections(&self.ibc_context, &request.try_into_domain()?)?.into_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)) + query_connection_client_state(&self.ibc_context, &request.try_into_domain()?)? + .into_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)) + query_connection_consensus_state(&self.ibc_context, &request.try_into_domain()?)? + .into_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)) + query_connection_params(&self.ibc_context, &request.into_domain())?.into_response() } } diff --git a/ibc-query/src/core/connection/types/mod.rs b/ibc-query/src/core/connection/types/mod.rs new file mode 100644 index 000000000..b8be6322b --- /dev/null +++ b/ibc-query/src/core/connection/types/mod.rs @@ -0,0 +1,5 @@ +mod request; +mod response; + +pub use request::*; +pub use response::*; diff --git a/ibc-query/src/core/connection/types/request.rs b/ibc-query/src/core/connection/types/request.rs new file mode 100644 index 000000000..ad11e62f5 --- /dev/null +++ b/ibc-query/src/core/connection/types/request.rs @@ -0,0 +1,161 @@ +//! Contains all the RPC method request domain types and their conversions to +//! and from the corresponding gRPC proto types for the connection module. + +use ibc::core::client::types::Height; +use ibc::core::host::types::identifiers::{ClientId, ConnectionId}; +use ibc::primitives::prelude::*; +use ibc_proto::ibc::core::channel::v1::QueryConnectionChannelsRequest as RawQueryConnectionChannelsRequest; +use ibc_proto::ibc::core::connection::v1::{ + QueryClientConnectionsRequest as RawQueryClientConnectionsRequest, + QueryConnectionClientStateRequest as RawQueryConnectionClientStateRequest, + QueryConnectionConsensusStateRequest as RawQueryConnectionConsensusStateRequest, + QueryConnectionParamsRequest as RawQueryConnectionParamsRequest, + QueryConnectionRequest as RawQueryConnectionRequest, + QueryConnectionsRequest as RawQueryConnectionsRequest, +}; + +use crate::error::QueryError; +use crate::types::PageRequest; + +/// Defines the RPC method request type for querying a connection. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConnectionRequest { + pub connection_id: ConnectionId, + pub query_height: Option, +} + +impl TryFrom for QueryConnectionRequest { + type Error = QueryError; + + fn try_from(request: RawQueryConnectionRequest) -> Result { + Ok(Self { + connection_id: request.connection_id.parse()?, + query_height: None, + }) + } +} + +/// Defines the RPC method request type for querying connections. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConnectionsRequest { + pub pagination: Option, +} + +impl From for QueryConnectionsRequest { + fn from(request: RawQueryConnectionsRequest) -> Self { + Self { + pagination: request.pagination.map(Into::into), + } + } +} + +/// Defines the RPC method request type for querying connections associated with +/// a client. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryClientConnectionsRequest { + pub client_id: ClientId, +} + +impl TryFrom for QueryClientConnectionsRequest { + type Error = QueryError; + + fn try_from(request: RawQueryClientConnectionsRequest) -> Result { + Ok(Self { + client_id: request.client_id.parse()?, + }) + } +} + +/// Defines the RPC method request type for querying the client state associated +/// with a connection. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConnectionClientStateRequest { + pub connection_id: ConnectionId, + pub query_height: Option, +} + +impl TryFrom for QueryConnectionClientStateRequest { + type Error = QueryError; + + fn try_from(request: RawQueryConnectionClientStateRequest) -> Result { + Ok(Self { + connection_id: request.connection_id.parse()?, + query_height: None, + }) + } +} + +/// Defines the RPC method request type for querying the consensus state +/// associated with a connection. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConnectionConsensusStateRequest { + pub connection_id: ConnectionId, + pub height: Height, + pub query_height: Option, +} + +impl TryFrom for QueryConnectionConsensusStateRequest { + type Error = QueryError; + + fn try_from(request: RawQueryConnectionConsensusStateRequest) -> Result { + Ok(Self { + connection_id: request.connection_id.parse()?, + height: Height::new(request.revision_number, request.revision_height)?, + query_height: None, + }) + } +} + +/// Defines the RPC method request type for querying the connection parameters. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConnectionParamsRequest { + pub query_height: Option, +} + +impl From for QueryConnectionParamsRequest { + fn from(_request: RawQueryConnectionParamsRequest) -> Self { + Self { query_height: None } + } +} + +/// Defines the RPC method request type for querying the channels associated +/// with a connection. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConnectionChannelsRequest { + pub connection_id: ConnectionId, + pub pagination: Option, +} + +impl TryFrom for QueryConnectionChannelsRequest { + type Error = QueryError; + + fn try_from(request: RawQueryConnectionChannelsRequest) -> Result { + Ok(Self { + connection_id: request.connection.parse()?, + pagination: request.pagination.map(Into::into), + }) + } +} + +impl From for RawQueryConnectionChannelsRequest { + fn from(request: QueryConnectionChannelsRequest) -> Self { + Self { + connection: request.connection_id.to_string(), + pagination: request.pagination.map(Into::into), + } + } +} diff --git a/ibc-query/src/core/connection/types/response.rs b/ibc-query/src/core/connection/types/response.rs new file mode 100644 index 000000000..5b252e594 --- /dev/null +++ b/ibc-query/src/core/connection/types/response.rs @@ -0,0 +1,336 @@ +//! Contains all the RPC method response domain types and their conversions to +//! and from the corresponding gRPC proto types for the connection module. + +use ibc::core::client::types::Height; +use ibc::core::connection::types::{ConnectionEnd, IdentifiedConnectionEnd}; +use ibc::core::host::types::identifiers::{ClientId, ConnectionId}; +use ibc::core::primitives::proto::Any; +use ibc::primitives::prelude::*; +use ibc::primitives::proto::Protobuf; +use ibc_proto::ibc::core::connection::v1::{ + Params as RawParams, QueryClientConnectionsResponse as RawQueryClientConnectionsResponse, + QueryConnectionClientStateResponse as RawQueryConnectionClientStateResponse, + QueryConnectionConsensusStateResponse as RawQueryConnectionConsensusStateResponse, + QueryConnectionParamsResponse as RawQueryConnectionParamsResponse, + QueryConnectionResponse as RawQueryConnectionResponse, + QueryConnectionsResponse as RawQueryConnectionsResponse, +}; + +use crate::core::client::IdentifiedClientState; +use crate::error::QueryError; +use crate::types::{PageResponse, Proof}; + +/// Defines the RPC method response type when querying a connection. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConnectionResponse { + pub conn_end: ConnectionEnd, + pub proof: Proof, + pub proof_height: Height, +} + +impl QueryConnectionResponse { + pub fn new(connection: ConnectionEnd, proof: Proof, proof_height: Height) -> Self { + Self { + conn_end: connection, + proof, + proof_height, + } + } +} + +impl Protobuf for QueryConnectionResponse {} + +impl TryFrom for QueryConnectionResponse { + type Error = QueryError; + + fn try_from(value: RawQueryConnectionResponse) -> Result { + Ok(Self { + conn_end: value + .connection + .ok_or_else(|| QueryError::missing_field("connection"))? + .try_into()?, + proof: value.proof, + proof_height: value + .proof_height + .ok_or_else(|| QueryError::missing_field("proof_height"))? + .try_into()?, + }) + } +} + +impl From for RawQueryConnectionResponse { + fn from(response: QueryConnectionResponse) -> Self { + Self { + connection: Some(response.conn_end.into()), + proof: response.proof, + proof_height: Some(response.proof_height.into()), + } + } +} + +/// Defines the RPC method response type when querying all the existing connection ends. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConnectionsResponse { + pub connections: Vec, + pub query_height: Height, + pub pagination: Option, +} + +impl QueryConnectionsResponse { + pub fn new( + connections: Vec, + query_height: Height, + pagination: Option, + ) -> Self { + Self { + connections, + query_height, + pagination, + } + } +} + +impl Protobuf for QueryConnectionsResponse {} + +impl TryFrom for QueryConnectionsResponse { + type Error = QueryError; + + fn try_from(value: RawQueryConnectionsResponse) -> Result { + Ok(Self { + connections: value + .connections + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + query_height: value + .height + .ok_or_else(|| QueryError::missing_field("height"))? + .try_into()?, + pagination: value.pagination.map(Into::into), + }) + } +} + +impl From for RawQueryConnectionsResponse { + fn from(response: QueryConnectionsResponse) -> Self { + Self { + connections: response.connections.into_iter().map(Into::into).collect(), + height: Some(response.query_height.into()), + pagination: response.pagination.map(Into::into), + } + } +} + +/// Defines the RPC method response type when querying client state associated +/// with a connection. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConnectionClientStateResponse { + pub identified_client_state: IdentifiedClientState, + pub proof: Proof, + pub proof_height: Height, +} + +impl QueryConnectionClientStateResponse { + pub fn new( + identified_client_state: IdentifiedClientState, + proof: Proof, + proof_height: Height, + ) -> Self { + Self { + identified_client_state, + proof, + proof_height, + } + } +} + +impl Protobuf for QueryConnectionClientStateResponse {} + +impl TryFrom for QueryConnectionClientStateResponse { + type Error = QueryError; + + fn try_from(value: RawQueryConnectionClientStateResponse) -> Result { + Ok(Self { + identified_client_state: value + .identified_client_state + .ok_or_else(|| QueryError::missing_field("identified_client_state"))? + .try_into()?, + proof: value.proof, + proof_height: value + .proof_height + .ok_or_else(|| QueryError::missing_field("proof_height"))? + .try_into()?, + }) + } +} + +impl From for RawQueryConnectionClientStateResponse { + fn from(response: QueryConnectionClientStateResponse) -> Self { + Self { + identified_client_state: Some(response.identified_client_state.into()), + proof: response.proof, + proof_height: Some(response.proof_height.into()), + } + } +} + +/// Defines the RPC method response type when querying all the existing +/// connection ends for a given client. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryClientConnectionsResponse { + pub connection_paths: Vec, + pub proof: Proof, + pub proof_height: Height, +} + +impl QueryClientConnectionsResponse { + pub fn new(connection_paths: Vec, proof: Proof, proof_height: Height) -> Self { + Self { + connection_paths, + proof, + proof_height, + } + } +} + +impl Protobuf for QueryClientConnectionsResponse {} + +impl TryFrom for QueryClientConnectionsResponse { + type Error = QueryError; + + fn try_from(value: RawQueryClientConnectionsResponse) -> Result { + Ok(Self { + connection_paths: value + .connection_paths + .into_iter() + .map(|id| id.parse()) + .collect::>()?, + proof: value.proof, + proof_height: value + .proof_height + .ok_or_else(|| QueryError::missing_field("proof_height"))? + .try_into()?, + }) + } +} + +impl From for RawQueryClientConnectionsResponse { + fn from(response: QueryClientConnectionsResponse) -> Self { + Self { + connection_paths: response + .connection_paths + .into_iter() + .map(|id| id.to_string()) + .collect(), + proof: response.proof, + proof_height: Some(response.proof_height.into()), + } + } +} + +/// Defines the RPC method response type when querying the consensus state for a +/// connection. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConnectionConsensusStateResponse { + pub consensus_state: Any, + pub client_id: ClientId, + pub proof: Proof, + pub proof_height: Height, +} + +impl QueryConnectionConsensusStateResponse { + pub fn new( + consensus_state: Any, + client_id: ClientId, + proof: Proof, + proof_height: Height, + ) -> Self { + Self { + consensus_state, + client_id, + proof, + proof_height, + } + } +} + +impl Protobuf for QueryConnectionConsensusStateResponse {} + +impl TryFrom for QueryConnectionConsensusStateResponse { + type Error = QueryError; + + fn try_from(value: RawQueryConnectionConsensusStateResponse) -> Result { + Ok(Self { + consensus_state: value + .consensus_state + .ok_or_else(|| QueryError::missing_field("consensus_state"))?, + client_id: value.client_id.parse()?, + proof: value.proof, + proof_height: value + .proof_height + .ok_or_else(|| QueryError::missing_field("proof_height"))? + .try_into()?, + }) + } +} + +impl From for RawQueryConnectionConsensusStateResponse { + fn from(response: QueryConnectionConsensusStateResponse) -> Self { + Self { + consensus_state: Some(response.consensus_state), + client_id: response.client_id.to_string(), + proof: response.proof, + proof_height: Some(response.proof_height.into()), + } + } +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct QueryConnectionParamsResponse { + pub max_expected_time_per_block: u64, +} + +impl QueryConnectionParamsResponse { + pub fn new(max_expected_time_per_block: u64) -> Self { + Self { + max_expected_time_per_block, + } + } +} + +impl Protobuf for QueryConnectionParamsResponse {} + +impl TryFrom for QueryConnectionParamsResponse { + type Error = QueryError; + + fn try_from(value: RawQueryConnectionParamsResponse) -> Result { + Ok(Self { + max_expected_time_per_block: value + .params + .ok_or_else(|| QueryError::missing_field("params"))? + .max_expected_time_per_block, + }) + } +} + +impl From for RawQueryConnectionParamsResponse { + fn from(response: QueryConnectionParamsResponse) -> Self { + Self { + params: Some(RawParams { + max_expected_time_per_block: response.max_expected_time_per_block, + }), + } + } +} diff --git a/ibc-query/src/error.rs b/ibc-query/src/error.rs index 9b8eb54f2..6d11fd2d8 100644 --- a/ibc-query/src/error.rs +++ b/ibc-query/src/error.rs @@ -1,7 +1,9 @@ use alloc::string::{String, ToString}; use displaydoc::Display; +use ibc::core::channel::types::error::{ChannelError, PacketError}; use ibc::core::client::types::error::ClientError; +use ibc::core::connection::types::error::ConnectionError; use ibc::core::handler::types::error::ContextError; use ibc::core::host::types::error::IdentifierError; use tonic::Status; @@ -10,39 +12,67 @@ use tonic::Status; 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 }, + /// Proof not found: {0} + ProofNotFound(String), + /// Missing field: {0} + MissingField(String), +} + +impl QueryError { + pub fn proof_not_found(description: T) -> Self { + Self::ProofNotFound(description.to_string()) + } + + pub fn missing_field(description: T) -> Self { + Self::MissingField(description.to_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), + QueryError::ContextError(ctx_err) => Self::internal(ctx_err.to_string()), + QueryError::IdentifierError(id_err) => Self::internal(id_err.to_string()), + QueryError::ProofNotFound(description) => Self::not_found(description), + QueryError::MissingField(description) => Self::invalid_argument(description), } } } impl From for QueryError { fn from(e: ContextError) -> Self { - QueryError::ContextError(e) + Self::ContextError(e) } } impl From for QueryError { fn from(e: ClientError) -> Self { - QueryError::ClientError(e) + Self::ContextError(ContextError::ClientError(e)) + } +} + +impl From for QueryError { + fn from(e: ConnectionError) -> Self { + Self::ContextError(ContextError::ConnectionError(e)) + } +} + +impl From for QueryError { + fn from(e: ChannelError) -> Self { + Self::ContextError(ContextError::ChannelError(e)) + } +} + +impl From for QueryError { + fn from(e: PacketError) -> Self { + Self::ContextError(ContextError::PacketError(e)) } } impl From for QueryError { fn from(e: IdentifierError) -> Self { - QueryError::IdentifierError(e) + Self::IdentifierError(e) } } diff --git a/ibc-query/src/lib.rs b/ibc-query/src/lib.rs index e9db0806f..504298398 100644 --- a/ibc-query/src/lib.rs +++ b/ibc-query/src/lib.rs @@ -66,5 +66,10 @@ extern crate alloc; +#[cfg(feature = "std")] +extern crate std; + pub mod core; pub mod error; +pub mod types; +pub mod utils; diff --git a/ibc-query/src/types.rs b/ibc-query/src/types.rs new file mode 100644 index 000000000..147307a98 --- /dev/null +++ b/ibc-query/src/types.rs @@ -0,0 +1,94 @@ +use ibc::core::primitives::prelude::*; +use ibc_proto::cosmos::base::query::v1beta1::{ + PageRequest as RawPageRequest, PageResponse as RawPageResponse, +}; + +pub type Proof = Vec; + +#[derive(Clone, Debug, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct PageRequest { + /// key is a value returned in PageResponse.next_key to begin + /// querying the next page most efficiently. Only one of offset or key + /// should be set. + pub key: Vec, + /// offset is a numeric offset that can be used when key is unavailable. + /// It is less efficient than using key. Only one of offset or key should + /// be set. + pub offset: u64, + /// limit is the total number of results to be returned in the result page. + /// If left empty it will default to a value to be set by each app. + pub limit: u64, + /// count_total is set to true to indicate that the result set should include + /// a count of the total number of items available for pagination in UIs. + /// count_total is only respected when offset is used. It is ignored when key + /// is set. + pub count_total: bool, + /// reverse is set to true if results are to be returned in the descending order. + pub reverse: bool, +} + +impl PageRequest { + pub fn all() -> Self { + Self { + limit: u64::MAX, + ..Default::default() + } + } +} + +impl From for RawPageRequest { + fn from(request: PageRequest) -> Self { + Self { + key: request.key, + offset: request.offset, + limit: request.limit, + count_total: request.count_total, + reverse: request.reverse, + } + } +} + +impl From for PageRequest { + fn from(request: RawPageRequest) -> Self { + Self { + key: request.key, + offset: request.offset, + limit: request.limit, + count_total: request.count_total, + reverse: request.reverse, + } + } +} + +#[derive(Clone, Debug, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct PageResponse { + /// next_key is the key to be passed to PageRequest.key to + /// query the next page most efficiently. It will be empty if + /// there are no more results. + pub next_key: Vec, + /// total is total number of results available if PageRequest.count_total + /// was set, its value is undefined otherwise + pub total: u64, +} + +impl From for RawPageResponse { + fn from(response: PageResponse) -> Self { + Self { + next_key: response.next_key, + total: response.total, + } + } +} + +impl From for PageResponse { + fn from(response: RawPageResponse) -> Self { + Self { + next_key: response.next_key, + total: response.total, + } + } +} diff --git a/ibc-query/src/utils.rs b/ibc-query/src/utils.rs new file mode 100644 index 000000000..7b1adeebe --- /dev/null +++ b/ibc-query/src/utils.rs @@ -0,0 +1,40 @@ +use tonic::{Request, Response, Status}; + +use crate::error::QueryError; + +pub trait TryIntoDomain { + fn try_into_domain(self) -> Result; +} + +pub trait IntoDomain { + fn into_domain(self) -> T; +} + +impl TryIntoDomain for Request +where + T: TryFrom, +{ + fn try_into_domain(self) -> Result { + Ok(self.into_inner().try_into()?) + } +} + +impl IntoDomain for Request +where + T: From, +{ + fn into_domain(self) -> T { + self.into_inner().into() + } +} + +pub trait IntoResponse: Sized +where + Self: Into, +{ + fn into_response(self) -> Result, Status> { + Ok(Response::new(self.into())) + } +} + +impl IntoResponse for T where T: Into {}