diff --git a/modules/src/error.rs b/modules/src/error.rs index dc91e9bf3f..ecc7981a80 100644 --- a/modules/src/error.rs +++ b/modules/src/error.rs @@ -16,6 +16,9 @@ pub enum Kind { /// Response parsing error #[error("Could not parse/unmarshall response")] ResponseParsing, + + #[error("Empty response value")] + EmptyResponseValue, } impl Kind { diff --git a/modules/src/ics02_client/query.rs b/modules/src/ics02_client/query.rs index ceb45147a7..4c4d5e7f10 100644 --- a/modules/src/ics02_client/query.rs +++ b/modules/src/ics02_client/query.rs @@ -1,15 +1,10 @@ use std::marker::PhantomData; -use tendermint_rpc::endpoint::abci_query::AbciQuery; - -use tendermint::abci; use crate::ics23_commitment::{CommitmentPath, CommitmentProof}; -use crate::error; -use crate::ics02_client::state::{ClientState, ConsensusState}; +//use crate::ics02_client::state::{ClientState, ConsensusState}; use crate::ics24_host::identifier::ClientId; -use crate::path::{ClientStatePath, ConsensusStatePath, Path}; -use crate::query::{IbcQuery, IbcResponse}; +use crate::path::{ClientStatePath, ConsensusStatePath}; use crate::Height; pub struct QueryClientFullState { @@ -32,29 +27,6 @@ impl QueryClientFullState { } } -impl IbcQuery for QueryClientFullState -where - CLS: ClientState, -{ - type Response = ClientFullStateResponse; - - fn path(&self) -> abci::Path { - "/store/ibc/key".parse().unwrap() - } - - fn height(&self) -> Height { - self.chain_height - } - - fn prove(&self) -> bool { - self.prove - } - - fn data(&self) -> Vec { - self.client_state_path.to_key().into() - } -} - pub struct ClientFullStateResponse { pub client_state: CLS, pub proof: Option, @@ -80,27 +52,6 @@ impl ClientFullStateResponse { } } -impl IbcResponse> for ClientFullStateResponse -where - CLS: ClientState, -{ - fn from_abci_response( - query: QueryClientFullState, - response: AbciQuery, - ) -> Result { - Ok(ClientFullStateResponse::new( - query.client_id, - amino_unmarshal_binary_length_prefixed(&response.value)?, - response.proof, - response.height.into(), - )) - } -} - -fn amino_unmarshal_binary_length_prefixed(_bytes: &[u8]) -> Result { - todo!() -} - pub struct QueryClientConsensusState { pub chain_height: Height, pub client_id: ClientId, @@ -128,29 +79,6 @@ impl QueryClientConsensusState { } } -impl IbcQuery for QueryClientConsensusState -where - CS: ConsensusState, -{ - type Response = ConsensusStateResponse; - - fn path(&self) -> abci::Path { - "/store/ibc/key".parse().unwrap() - } - - fn height(&self) -> Height { - self.chain_height - } - - fn prove(&self) -> bool { - self.prove - } - - fn data(&self) -> Vec { - self.consensus_state_path.to_key().into() - } -} - pub struct ConsensusStateResponse { pub consensus_state: CS, pub proof: Option, @@ -176,20 +104,3 @@ impl ConsensusStateResponse { } } } - -impl IbcResponse> for ConsensusStateResponse -where - CS: ConsensusState, -{ - fn from_abci_response( - query: QueryClientConsensusState, - response: AbciQuery, - ) -> Result { - Ok(ConsensusStateResponse::new( - query.client_id, - amino_unmarshal_binary_length_prefixed(&response.value)?, - response.proof, - response.height.into(), - )) - } -} diff --git a/modules/src/ics03_connection/connection.rs b/modules/src/ics03_connection/connection.rs index 43368723a2..09e5d9fb27 100644 --- a/modules/src/ics03_connection/connection.rs +++ b/modules/src/ics03_connection/connection.rs @@ -2,11 +2,13 @@ use super::exported::*; use crate::ics03_connection::error::{Error, Kind}; use crate::ics23_commitment::CommitmentPrefix; use crate::ics24_host::identifier::{ClientId, ConnectionId}; +use crate::try_from_raw::TryFromRaw; use serde_derive::{Deserialize, Serialize}; // Import proto declarations. -use ibc_proto::connection::ConnectionEnd as ProtoConnectionEnd; -use ibc_proto::connection::Counterparty as ProtoCounterparty; +use ibc_proto::connection::ConnectionEnd as RawConnectionEnd; +use ibc_proto::connection::Counterparty as RawCounterparty; +use std::convert::TryFrom; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ConnectionEnd { @@ -16,43 +18,31 @@ pub struct ConnectionEnd { versions: Vec, } -impl ConnectionEnd { - pub fn new( - client_id: ClientId, - counterparty: Counterparty, - versions: Vec, - ) -> Result { - Ok(Self { - state: State::Uninitialized, - client_id, - counterparty, - versions: validate_versions(versions).map_err(|e| Kind::InvalidVersion.context(e))?, - }) - } - - pub fn set_state(&mut self, new_state: State) { - self.state = new_state; - } - - pub fn from_proto_connection_end(pc: ProtoConnectionEnd) -> Result { - if pc.id == "" { +impl TryFromRaw for ConnectionEnd { + type RawType = RawConnectionEnd; + type Error = anomaly::Error; + fn try_from(value: RawConnectionEnd) -> Result { + // Todo: Is validation complete here? (Code was moved from `from_proto_connection_end`.) + if value.id == "" { return Err(Kind::ConnectionNotFound.into()); } // The Counterparty field is an Option, may be missing. - match pc.counterparty { + match value.counterparty { Some(cp) => { let mut conn = ConnectionEnd::new( - pc.client_id + value + .client_id .parse() .map_err(|e| Kind::IdentifierError.context(e))?, - Counterparty::from_proto_counterparty(cp)?, - validate_versions(pc.versions).map_err(|e| Kind::InvalidVersion.context(e))?, + Counterparty::try_from(cp)?, + validate_versions(value.versions) + .map_err(|e| Kind::InvalidVersion.context(e))?, ) .unwrap(); // Set the state. - conn.set_state(State::from_i32(pc.state)); + conn.set_state(State::from_i32(value.state)); Ok(conn) } @@ -62,6 +52,25 @@ impl ConnectionEnd { } } +impl ConnectionEnd { + pub fn new( + client_id: ClientId, + counterparty: Counterparty, + versions: Vec, + ) -> Result { + Ok(Self { + state: State::Uninitialized, + client_id, + counterparty, + versions: validate_versions(versions).map_err(|e| Kind::InvalidVersion.context(e))?, + }) + } + + pub fn set_state(&mut self, new_state: State) { + self.state = new_state; + } +} + impl Connection for ConnectionEnd { type ValidationError = Error; @@ -95,6 +104,22 @@ pub struct Counterparty { prefix: CommitmentPrefix, } +impl TryFrom for Counterparty { + type Error = anomaly::Error; + + fn try_from(value: RawCounterparty) -> Result { + // Todo: Is validation complete here? (code was moved from `from_proto_counterparty`) + match value.prefix { + Some(prefix) => Counterparty::new( + value.client_id, + value.connection_id, + CommitmentPrefix::new(prefix.key_prefix), + ), + None => Err(Kind::MissingCounterpartyPrefix.into()), + } + } +} + impl Counterparty { pub fn new( client_id: String, @@ -111,17 +136,6 @@ impl Counterparty { prefix, }) } - - pub fn from_proto_counterparty(pc: ProtoCounterparty) -> Result { - match pc.prefix { - Some(prefix) => Counterparty::new( - pc.client_id, - pc.connection_id, - CommitmentPrefix::new(prefix.key_prefix), - ), - None => Err(Kind::MissingCounterpartyPrefix.into()), - } - } } impl ConnectionCounterparty for Counterparty { diff --git a/modules/src/ics03_connection/mod.rs b/modules/src/ics03_connection/mod.rs index 449a5c1a66..fadbbda45a 100644 --- a/modules/src/ics03_connection/mod.rs +++ b/modules/src/ics03_connection/mod.rs @@ -5,4 +5,3 @@ pub mod error; pub mod events; pub mod exported; pub mod msgs; -pub mod query; diff --git a/modules/src/ics03_connection/query.rs b/modules/src/ics03_connection/query.rs deleted file mode 100644 index b4108df291..0000000000 --- a/modules/src/ics03_connection/query.rs +++ /dev/null @@ -1,121 +0,0 @@ -use tendermint_rpc::endpoint::abci_query::AbciQuery; - -use tendermint::abci; - -use crate::ics23_commitment::{CommitmentPath, CommitmentProof}; -use crate::ics24_host::identifier::ConnectionId; - -use crate::error; -use crate::ics03_connection::connection::ConnectionEnd; -use crate::path::{ConnectionPath, Path}; -use crate::query::{IbcQuery, IbcResponse}; -use crate::Height; - -use crate::ics03_connection::error::Error; - -// Import protobuf definitions. -use ibc_proto::connection::ConnectionEnd as ProtoConnectionEnd; - -use bytes::Bytes; -use prost::Message; - -pub struct QueryConnection { - pub chain_height: Height, - pub connection_id: ConnectionId, - pub connection_path: ConnectionPath, - pub prove: bool, -} - -impl QueryConnection { - pub fn new(chain_height: Height, connection_id: ConnectionId, prove: bool) -> Self { - Self { - chain_height, - connection_id: connection_id.clone(), - connection_path: ConnectionPath::new(connection_id), - prove, - } - } -} - -impl IbcQuery for QueryConnection { - type Response = ConnectionResponse; - - fn path(&self) -> abci::Path { - "/store/ibc/key".parse().unwrap() - } - - fn height(&self) -> Height { - self.chain_height - } - - fn prove(&self) -> bool { - self.prove - } - - fn data(&self) -> Vec { - self.connection_path.to_key().into() - } -} - -pub struct ConnectionResponse { - pub connection: IdentifiedConnectionEnd, - pub proof: Option, - pub proof_path: CommitmentPath, - pub proof_height: Height, -} - -impl ConnectionResponse { - pub fn new( - connection_id: ConnectionId, - connection: ConnectionEnd, - abci_proof: Option, - proof_height: Height, - ) -> Self { - let proof_path = CommitmentPath::from_path(ConnectionPath::new(connection_id.clone())); - let identified_connection_end = IdentifiedConnectionEnd::new(connection, connection_id); - ConnectionResponse { - connection: identified_connection_end, - proof: abci_proof, - proof_path, - proof_height, - } - } -} - -impl IbcResponse for ConnectionResponse { - fn from_abci_response( - query: QueryConnection, - response: AbciQuery, - ) -> Result { - match proto_unmarshal(response.value) { - Ok(decoded_conn) => Ok(ConnectionResponse::new( - query.connection_id, - decoded_conn, - response.proof, - response.height.into(), - )), - Err(e) => Err(error::Kind::ResponseParsing.context(e).into()), - } - } -} - -#[derive(Debug)] -pub struct IdentifiedConnectionEnd { - connection_end: ConnectionEnd, - connection_id: ConnectionId, -} - -impl IdentifiedConnectionEnd { - pub fn new(connection_end: ConnectionEnd, connection_id: ConnectionId) -> Self { - IdentifiedConnectionEnd { - connection_end, - connection_id, - } - } -} - -fn proto_unmarshal(bytes: Vec) -> Result { - let buf = Bytes::from(bytes); - let decoded = ProtoConnectionEnd::decode(buf).unwrap(); - ConnectionEnd::from_proto_connection_end(decoded) -} diff --git a/modules/src/ics04_channel/channel.rs b/modules/src/ics04_channel/channel.rs index 9c4bb86c7b..0b2d22fd39 100644 --- a/modules/src/ics04_channel/channel.rs +++ b/modules/src/ics04_channel/channel.rs @@ -1,9 +1,15 @@ use super::exported::*; use crate::ics04_channel::error; -use crate::ics04_channel::error::Kind; +use crate::ics04_channel::error::{Error, Kind}; use crate::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; +use crate::try_from_raw::TryFromRaw; use serde_derive::{Deserialize, Serialize}; +// Import proto declarations. +use ibc_proto::channel::Channel as RawChannel; + +use std::str::FromStr; + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ChannelEnd { state: State, @@ -13,6 +19,32 @@ pub struct ChannelEnd { version: String, } +impl TryFromRaw for ChannelEnd { + type RawType = RawChannel; + type Error = anomaly::Error; + fn try_from(value: RawChannel) -> Result { + // Todo: Do validation of data here. This is just an example implementation for testing. + let ordering = match value.ordering { + 0 => Order::None, + 1 => Order::Unordered, + 2 => Order::Ordered, + _ => panic!("invalid order number"), + }; + let counterparty = value.counterparty.unwrap(); + let remote = Counterparty { + port_id: PortId::from_str(counterparty.port_id.as_str()).unwrap(), + channel_id: ChannelId::from_str(counterparty.channel_id.as_str()).unwrap(), + }; + let connection_hops = value + .connection_hops + .into_iter() + .map(|e| ConnectionId::from_str(e.as_str()).unwrap()) + .collect(); + let version = value.version; + Ok(ChannelEnd::new(ordering, remote, connection_hops, version)) + } +} + impl ChannelEnd { pub fn new( ordering: Order, @@ -31,7 +63,7 @@ impl ChannelEnd { } impl Channel for ChannelEnd { - type ValidationError = crate::ics04_channel::error::Error; + type ValidationError = Error; fn state(&self) -> &State { &self.state @@ -77,10 +109,7 @@ pub struct Counterparty { } impl Counterparty { - pub fn new( - port_id: String, - channel_id: String, - ) -> Result { + pub fn new(port_id: String, channel_id: String) -> Result { Ok(Self { port_id: port_id .parse() @@ -93,7 +122,7 @@ impl Counterparty { } impl ChannelCounterparty for Counterparty { - type ValidationError = crate::ics04_channel::error::Error; + type ValidationError = Error; fn port_id(&self) -> String { self.port_id.as_str().into() diff --git a/modules/src/ics04_channel/mod.rs b/modules/src/ics04_channel/mod.rs index 1f5b28720f..259401108a 100644 --- a/modules/src/ics04_channel/mod.rs +++ b/modules/src/ics04_channel/mod.rs @@ -6,4 +6,3 @@ pub mod events; pub mod exported; pub mod msgs; pub mod packet; -pub mod query; diff --git a/modules/src/ics04_channel/query.rs b/modules/src/ics04_channel/query.rs deleted file mode 100644 index e256304086..0000000000 --- a/modules/src/ics04_channel/query.rs +++ /dev/null @@ -1,94 +0,0 @@ -use tendermint_rpc::endpoint::abci_query::AbciQuery; - -use tendermint::abci; - -use crate::ics23_commitment::{CommitmentPath, CommitmentProof}; - -use crate::error; -use crate::ics04_channel::channel::ChannelEnd; -use crate::ics24_host::identifier::{ChannelId, PortId}; -use crate::path::{ChannelEndsPath, Path}; -use crate::query::{IbcQuery, IbcResponse}; -use crate::Height; - -pub struct QueryChannel { - pub chain_height: Height, - pub port_id: PortId, - pub channel_id: ChannelId, - pub channel_ends_path: ChannelEndsPath, - pub prove: bool, -} - -impl QueryChannel { - pub fn new(chain_height: Height, port_id: PortId, channel_id: ChannelId, prove: bool) -> Self { - Self { - chain_height, - port_id: port_id.clone(), - channel_id: channel_id.clone(), - channel_ends_path: ChannelEndsPath::new(port_id, channel_id), - prove, - } - } -} - -impl IbcQuery for QueryChannel { - type Response = ChannelResponse; - - fn path(&self) -> abci::Path { - "/store/ibc/key".parse().unwrap() - } - - fn height(&self) -> Height { - self.chain_height - } - - fn prove(&self) -> bool { - self.prove - } - - fn data(&self) -> Vec { - self.channel_ends_path.to_key().into() - } -} - -pub struct ChannelResponse { - pub channel: ChannelEnd, - pub proof: Option, - pub proof_path: CommitmentPath, - pub proof_height: Height, -} - -impl ChannelResponse { - pub fn new( - port_id: PortId, - channel_id: ChannelId, - channel: ChannelEnd, - abci_proof: Option, - proof_height: Height, - ) -> Self { - let proof_path = CommitmentPath::from_path(ChannelEndsPath::new(port_id, channel_id)); - - ChannelResponse { - channel, - proof: abci_proof, - proof_path, - proof_height, - } - } -} - -impl IbcResponse for ChannelResponse { - fn from_abci_response(query: QueryChannel, response: AbciQuery) -> Result { - Ok(ChannelResponse::new( - query.port_id, - query.channel_id, - amino_unmarshal_binary_length_prefixed(&response.value)?, - response.proof, - response.height.into(), - )) - } -} - -fn amino_unmarshal_binary_length_prefixed(_bytes: &[u8]) -> Result { - todo!() -} diff --git a/modules/src/lib.rs b/modules/src/lib.rs index f677a36c5d..3e06fa9712 100644 --- a/modules/src/lib.rs +++ b/modules/src/lib.rs @@ -31,7 +31,7 @@ pub mod ics24_host; pub mod keys; pub mod path; pub mod proofs; -pub mod query; +pub mod try_from_raw; pub mod tx_msg; /// Height of a block, same as in `tendermint` crate diff --git a/modules/src/query.rs b/modules/src/query.rs deleted file mode 100644 index bc39635469..0000000000 --- a/modules/src/query.rs +++ /dev/null @@ -1,35 +0,0 @@ -use tendermint::abci; -use tendermint_rpc::endpoint::abci_query::AbciQuery; - -use crate::error; -use crate::Height; - -/// The type of IBC response sent back for a given IBC `Query`. -pub trait IbcResponse: Sized { - /// The type of the raw response returned by the interface used to query the chain - /// - /// TODO: Uncomment once we abstract over the IBC client - // type RawType; - - /// Build a response of this type from the initial `query` and the IBC `response`. - /// - /// TODO: Replace `AbciQuery` with `Self::RawType` - fn from_abci_response(query: Query, response: AbciQuery) -> Result; -} - -/// Defines an IBC query -pub trait IbcQuery: Sized { - type Response: IbcResponse; - - fn path(&self) -> abci::Path; - fn height(&self) -> Height; - fn prove(&self) -> bool; - fn data(&self) -> Vec; - - /// Build a `Response` from a raw `AbciQuery` response - /// - /// TODO: Replace `AbciQuery` with `>::RawType` - fn build_response(self, response: AbciQuery) -> Result { - Self::Response::from_abci_response(self, response) - } -} diff --git a/modules/src/try_from_raw.rs b/modules/src/try_from_raw.rs new file mode 100644 index 0000000000..51f5eddf33 --- /dev/null +++ b/modules/src/try_from_raw.rs @@ -0,0 +1,32 @@ +//! TryFromRaw trait for automatic protobuf deserialization - currently implemented with prost +//! This is similar to the pattern of using the #[serde(from="RawType") derive for automatic +//! conversion with the TryFrom::try_from(value: RawType) trait for validation. +//! Only serde does this for JSON and here we need to do it for protobuf. +use crate::error::{Error, Kind}; +use bytes::Bytes; +use prost::Message; +use std::convert::Into; +use std::default::Default; +use std::error::Error as StdError; +use std::marker::Sized; +use std::vec::Vec; + +/// TryFromRaw trait needs to implement a try_from() function and an Error type for the return of that function. +pub trait TryFromRaw: Sized { + /// RawType defines the prost-compiled protobuf-defined Rust struct + type RawType: Message + Default; + + /// Error defines the error type returned from validation. + type Error: Into>; + + /// try_from function will validate the incoming RawType and convert it to our domain type + fn try_from(value: Self::RawType) -> Result; + + /// deserialize function will deserialize from the protobuf-encoded bytes into the domain type + /// using the RawType and try_from() as an interim step. + fn deserialize(wire: Vec) -> Result { + Self::RawType::decode(Bytes::from(wire)) + .map_err(|e| Kind::ResponseParsing.context(e).into()) + .and_then(|r| Self::try_from(r).map_err(|e| Kind::ResponseParsing.context(e).into())) + } +} diff --git a/relayer/cli/src/commands/query/channel.rs b/relayer/cli/src/commands/query/channel.rs index 7f1cd460ac..6bca582ac7 100644 --- a/relayer/cli/src/commands/query/channel.rs +++ b/relayer/cli/src/commands/query/channel.rs @@ -2,13 +2,18 @@ use crate::prelude::*; use abscissa_core::{Command, Options, Runnable}; use relayer::config::{ChainConfig, Config}; -use relayer::query::channel::query_channel; +use relayer::query::{query, Request}; +use relayer_modules::ics04_channel::channel::ChannelEnd; use relayer_modules::ics24_host::identifier::{ChannelId, PortId}; use crate::commands::utils::block_on; use relayer::chain::tendermint::TendermintChain; +use relayer_modules::error::Error; use relayer_modules::ics24_host::error::ValidationError; +use relayer_modules::path::{ChannelEndsPath, Path}; +use std::str::FromStr; +use tendermint::abci::Path as TendermintPath; use tendermint::chain::Id as ChainId; #[derive(Clone, Command, Debug, Options)] @@ -37,6 +42,17 @@ struct QueryChannelOptions { proof: bool, } +impl Into for QueryChannelOptions { + fn into(self) -> Request { + Request { + path: Some(TendermintPath::from_str(&"store/ibc/key").unwrap()), + data: ChannelEndsPath::new(self.port_id, self.channel_id).to_string(), + height: self.height, + prove: self.proof, + } + } +} + impl QueryChannelEndCmd { fn validate_options( &self, @@ -94,25 +110,14 @@ impl Runnable for QueryChannelEndCmd { }; status_info!("Options", "{:?}", opts); - // run with proof: - // cargo run --bin relayer -- -c simple_config.toml query channel end ibc0 transfer ibconexfer - // // run without proof: - // cargo run --bin relayer -- -c simple_config.toml query channel end ibc0 transfer ibconexfer -p false - // - // Note: currently both fail in amino_unmarshal_binary_length_prefixed(). - // To test this start a Gaia node and configure a channel using the go relayer. + // cargo run --bin relayer -- -c relayer/relay/tests/config/fixtures/simple_config.toml query channel end ibc-test firstport firstchannel --height 3 -p false let chain = TendermintChain::from_config(chain_config).unwrap(); - let res = block_on(query_channel( - &chain, - opts.height, - opts.port_id.clone(), - opts.channel_id.clone(), - opts.proof, - )); + let res: Result = block_on(query(&chain, opts)); + match res { - Ok(cs) => status_info!("channel query result: ", "{:?}", cs.channel), - Err(e) => status_info!("channel query error: ", "{:?}", e), + Ok(cs) => status_info!("channel query result: ", "{:?}", cs), + Err(e) => status_info!("channel query error", "{}", e), } } } diff --git a/relayer/cli/src/commands/query/client.rs b/relayer/cli/src/commands/query/client.rs index 819e0763d2..64d866d285 100644 --- a/relayer/cli/src/commands/query/client.rs +++ b/relayer/cli/src/commands/query/client.rs @@ -2,11 +2,13 @@ use crate::prelude::*; use abscissa_core::{Command, Options, Runnable}; use relayer::config::{ChainConfig, Config}; -use relayer::query::client::{query_client_consensus_state, query_client_full_state}; +//use relayer::query::client::{query_client_consensus_state, query_client_full_state}; +//use relayer::query::{query, Request}; +//use relayer_modules::ics02_client::query::QueryClientFullState; use relayer_modules::ics24_host::identifier::ClientId; -use crate::commands::utils::block_on; +//use crate::commands::utils::block_on; use relayer::chain::tendermint::TendermintChain; use relayer_modules::ics24_host::error::ValidationError; use tendermint::chain::Id as ChainId; @@ -70,14 +72,15 @@ impl Runnable for QueryClientStateCmd { status_info!("Options", "{:?}", opts); // run with proof: - // cargo run --bin relayer -- -c simple_config.toml query client state ibc0 ibconeclient + // cargo run --bin relayer -- -c relayer/relay/tests/config/fixtures/simple_config.toml query client state ibc-test ethbridge --height 3 // // run without proof: - // cargo run --bin relayer -- -c simple_config.toml query client state ibc0 ibconeclient -p false + // cargo run --bin relayer -- -c relayer/relay/tests/config/fixtures/simple_config.toml query client state ibc-test ethbridge --height 3 -p false // // Note: currently both fail in amino_unmarshal_binary_length_prefixed(). // To test this start a Gaia node and configure a client using the go relayer. - let chain = TendermintChain::from_config(chain_config).unwrap(); + let _chain = TendermintChain::from_config(chain_config).unwrap(); + /* Todo: Implement client full state query let res = block_on(query_client_full_state( &chain, opts.height, @@ -88,6 +91,7 @@ impl Runnable for QueryClientStateCmd { Ok(cs) => status_info!("client state query result: ", "{:?}", cs.client_state), Err(e) => status_info!("client state query error: ", "{:?}", e), } + */ } } @@ -167,7 +171,8 @@ impl Runnable for QueryClientConsensusCmd { // // Note: currently both fail in amino_unmarshal_binary_length_prefixed(). // To test this start a Gaia node and configure a client using the go relayer. - let chain = TendermintChain::from_config(chain_config).unwrap(); + let _chain = TendermintChain::from_config(chain_config).unwrap(); + /* Todo: Implement client consensus state query let res = block_on(query_client_consensus_state( &chain, opts.height, @@ -183,6 +188,8 @@ impl Runnable for QueryClientConsensusCmd { ), Err(e) => status_info!("client consensus state query error: ", "{:?}", e), } + + */ } } diff --git a/relayer/cli/src/commands/query/connection.rs b/relayer/cli/src/commands/query/connection.rs index af8a6f7fd5..4a2acedbaa 100644 --- a/relayer/cli/src/commands/query/connection.rs +++ b/relayer/cli/src/commands/query/connection.rs @@ -2,14 +2,20 @@ use crate::prelude::*; use abscissa_core::{Command, Options, Runnable}; use relayer::config::{ChainConfig, Config}; -use relayer::query::connection::query_connection; use crate::commands::utils::block_on; use relayer::chain::tendermint::TendermintChain; +use relayer::query::{query, Request}; +use relayer_modules::error::Error; use relayer_modules::ics24_host::error::ValidationError; use relayer_modules::ics24_host::identifier::ConnectionId; +use relayer_modules::path::{ConnectionPath, Path}; use tendermint::chain::Id as ChainId; +use relayer_modules::ics03_connection::connection::ConnectionEnd; +use std::str::FromStr; +use tendermint::abci::Path as TendermintPath; + #[derive(Clone, Command, Debug, Options)] pub struct QueryConnectionEndCmd { #[options(free, help = "identifier of the chain to query")] @@ -32,6 +38,17 @@ struct QueryConnectionOptions { proof: bool, } +impl Into for QueryConnectionOptions { + fn into(self) -> Request { + Request { + path: Some(TendermintPath::from_str(&"store/ibc/key").unwrap()), + data: ConnectionPath::new(self.connection_id).to_string(), + height: self.height, + prove: self.proof, + } + } +} + impl QueryConnectionEndCmd { fn validate_options( &self, @@ -82,23 +99,12 @@ impl Runnable for QueryConnectionEndCmd { status_info!("Options", "{:?}", opts); let chain = TendermintChain::from_config(chain_config).unwrap(); - // run with proof: - // cargo run --bin relayer -- -c simple_config.toml query connection end ibc0 ibconeconnection - // // run without proof: - // cargo run --bin relayer -- -c simple_config.toml query connection end ibc0 ibconeconnection -p false - // - // Note: currently both fail in amino_unmarshal_binary_length_prefixed(). - // To test this start a Gaia node and configure a client using the go relayer. - let res = block_on(query_connection( - &chain, - opts.height, - opts.connection_id.clone(), - opts.proof, - )); + // cargo run --bin relayer -- -c relayer/relay/tests/config/fixtures/simple_config.toml query connection end ibc-test connectionidone --height 3 -p false + let res: Result = block_on(query(&chain, opts)); match res { - Ok(cs) => status_info!("connection query result: ", "{:?}", cs.connection), + Ok(cs) => status_info!("connection query result: ", "{:?}", cs), Err(e) => status_info!("connection query error", "{}", e), } } diff --git a/relayer/relay/src/query.rs b/relayer/relay/src/query.rs index 7779505e4b..bf376adbf2 100644 --- a/relayer/relay/src/query.rs +++ b/relayer/relay/src/query.rs @@ -1,49 +1,78 @@ -use tendermint::abci; - use crate::chain::Chain; use relayer_modules::error; -use relayer_modules::query::IbcQuery; +use relayer_modules::try_from_raw::TryFromRaw; +use tendermint::abci::Path as TendermintPath; +use tendermint::block; -pub mod channel; pub mod client; -pub mod connection; -/// Perform an IBC `query` on the given `chain`, and return the corresponding IBC response. -pub async fn ibc_query(chain: &C, query: Q) -> Result +/// Query the ABCI application for information +pub struct Request { + /// Path to the data + pub path: Option, + + /// Data to query + pub data: String, + + /// Block height + pub height: u64, + + /// Include proof in response + pub prove: bool, +} + +/// Whether or not this path requires proof verification. +/// +/// is_query_store_with_proofxpects a format like ///, +/// where queryType must be "store" and subpath must be "key" to require a proof. +fn is_query_store_with_proof(_path: &TendermintPath) -> bool { + false +} + +/// Perform a generic `abci_query` on the given `chain`, and return the corresponding deserialized response data. +pub async fn query(chain: &C, request: O) -> Result where - C: Chain, - Q: IbcQuery, + C: Chain, // Chain configuration + T: TryFromRaw, // Internal Struct type (expected response) + O: Into, // Query Command configuration (opts) { - let abci_response = chain + // RPC Request + + let request: Request = request.into(); + let path = request.path.clone().unwrap(); // for the is_query_store_with_proof function + + // Use the Tendermint-rs RPC client to do the query - Todo: generalize further for other type of chains + let response = chain .rpc_client() .abci_query( - Some(query.path()), - query.data(), - Some(query.height().into()), - query.prove(), + request.path, + request.data.to_string().into_bytes(), + match request.height { + 0 => None, + _ => Some(block::Height::from(request.height)), + }, + request.prove, ) .await .map_err(|e| error::Kind::Rpc.context(e))?; - if !abci_response.code.is_ok() { + if !response.code.is_ok() { // Fail with response log - return Err(error::Kind::Rpc - .context(abci_response.log.to_string()) - .into()); + return Err(error::Kind::Rpc.context(response.log.to_string()).into()); } + if response.value.is_empty() { + // Fail due to empty response value (nothing to decode). + return Err(error::Kind::EmptyResponseValue.into()); + } + + // Verify response proof // Data that is not from trusted node or subspace query needs verification - if is_query_store_with_proof(&query.path()) { + if is_query_store_with_proof(&path) { todo!() // TODO: Verify proof } - query.build_response(abci_response) -} + // Deserialize response data -/// Whether or not this path requires proof verification. -/// -/// is_query_store_with_proofxpects a format like ///, -/// where queryType must be "store" and subpath must be "key" to require a proof. -fn is_query_store_with_proof(_path: &abci::Path) -> bool { - false + T::deserialize(response.value) } diff --git a/relayer/relay/src/query/channel.rs b/relayer/relay/src/query/channel.rs deleted file mode 100644 index 588efbfa53..0000000000 --- a/relayer/relay/src/query/channel.rs +++ /dev/null @@ -1,22 +0,0 @@ -use relayer_modules::ics24_host::identifier::{ChannelId, PortId}; -use relayer_modules::Height; - -use super::ibc_query; -use crate::chain::Chain; -use relayer_modules::ics04_channel::query::{ChannelResponse, QueryChannel}; - -use relayer_modules::error; - -pub async fn query_channel( - chain: &C, - chain_height: Height, - port_id: PortId, - channel_id: ChannelId, - prove: bool, -) -> Result -where - C: Chain, -{ - let query = QueryChannel::new(chain_height, port_id, channel_id, prove); - ibc_query(chain, query).await -} diff --git a/relayer/relay/src/query/client.rs b/relayer/relay/src/query/client.rs index 74b082c32d..aaea6e4fa9 100644 --- a/relayer/relay/src/query/client.rs +++ b/relayer/relay/src/query/client.rs @@ -1,36 +1,60 @@ use relayer_modules::ics24_host::identifier::ClientId; use relayer_modules::Height; -use super::ibc_query; use crate::chain::Chain; -use relayer_modules::ics02_client::query::{ClientFullStateResponse, QueryClientFullState}; -use relayer_modules::ics02_client::query::{ConsensusStateResponse, QueryClientConsensusState}; +use relayer_modules::ics02_client::query::{ClientFullStateResponse, ConsensusStateResponse}; use relayer_modules::error; pub async fn query_client_full_state( - chain: &C, - chain_height: Height, - client_id: ClientId, - prove: bool, + _chain: &C, + _chain_height: Height, + _client_id: ClientId, + _prove: bool, ) -> Result, error::Error> where C: Chain, { - let query = QueryClientFullState::new(chain_height, client_id, prove); - ibc_query(chain, query).await + todo!("Implement generic query for client") + /* + let _query = QueryClientFullState::new(chain_height, client_id, prove); + ibc_query( + chain, + Request { + path: None, + data: vec![], + height: Some(TendermintHeight::from(chain_height)), + prove, + }, + query, + ) + .await + */ } pub async fn query_client_consensus_state( - chain: &C, - chain_height: Height, - client_id: ClientId, - consensus_height: Height, - prove: bool, + _chain: &C, + _chain_height: Height, + _client_id: ClientId, + _consensus_height: Height, + _prove: bool, ) -> Result, error::Error> where C: Chain, { - let query = QueryClientConsensusState::new(chain_height, client_id, consensus_height, prove); - ibc_query(chain, query).await + todo!("Implement generic query for client") + /* + let _query = QueryClientConsensusState::new(chain_height, client_id, consensus_height, prove); + ibc_query( + chain, + Request { + path: None, + data: vec![], + height: Some(TendermintHeight::from(chain_height)), + prove, + }, + query, + ) + .await + */ } diff --git a/relayer/relay/src/query/connection.rs b/relayer/relay/src/query/connection.rs deleted file mode 100644 index 1cacf5cf04..0000000000 --- a/relayer/relay/src/query/connection.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::chain::Chain; -use relayer_modules::Height; - -use super::ibc_query; -use relayer_modules::ics03_connection::query::{ConnectionResponse, QueryConnection}; -use relayer_modules::ics24_host::identifier::ConnectionId; - -use relayer_modules::error; - -pub async fn query_connection( - chain: &C, - chain_height: Height, - connection_id: ConnectionId, - prove: bool, -) -> Result -where - C: Chain, -{ - let query = QueryConnection::new(chain_height, connection_id, prove); - ibc_query(chain, query).await -}