forked from informalsystems/hermes
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement QueryClientConsensusState IBC query (informalsystems#23)
Summary: Implement the `QueryClientConsensusState` IBC query, some of its dependencies, and a mostly type-safe IBC wrapper around `tendermint::rpc::Client`. Notes: - Comments/documentation is currently missing. - There is still a bunch of function/trait stubs with `todo!()` bodies. - Some functions contains calls to `unwrap()` which may panic. - Commitment proofs verification is not implemented. The related data structures are only stubs before we link against the [`ics23`](https://github.com/confio/ics23) crate. - Amino deserialization of the IBC query response value is not implemented. - This commit depends on some changes to the `tendermint-rs` crate, which are available in [my fork](https://github.com/romac/tendermint-rs/compare/rpc-client-new-sync).
- Loading branch information
Showing
8 changed files
with
233 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
use ::tendermint::rpc; | ||
|
||
use relayer_modules::ics02_client::state::ConsensusState; | ||
|
||
use crate::config::ChainConfig; | ||
|
||
pub mod tendermint; | ||
|
||
pub trait Chain { | ||
type Type; | ||
type ConsensusState: ConsensusState; | ||
|
||
fn config(&self) -> &ChainConfig; | ||
fn rpc_client(&self) -> &rpc::Client; // TODO: Define our own generic client interface? | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
use tendermint::rpc; | ||
|
||
use relayer_modules::ics02_client::client_type::Tendermint; | ||
use relayer_modules::ics07_tendermint::consensus_state::ConsensusState; | ||
|
||
use crate::config::ChainConfig; | ||
use crate::error; | ||
|
||
use super::Chain; | ||
|
||
pub struct TendermintChain { | ||
config: ChainConfig, | ||
rpc_client: rpc::Client, | ||
} | ||
|
||
impl TendermintChain { | ||
pub fn from_config(config: ChainConfig) -> Result<Self, error::Error> { | ||
let rpc_client = rpc::Client::new(config.rpc_addr.clone()); | ||
Ok(Self { config, rpc_client }) | ||
} | ||
} | ||
|
||
impl Chain for TendermintChain { | ||
type Type = Tendermint; | ||
type ConsensusState = ConsensusState; | ||
|
||
fn config(&self) -> &ChainConfig { | ||
&self.config | ||
} | ||
|
||
fn rpc_client(&self) -> &rpc::Client { | ||
&self.rpc_client | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,5 +11,7 @@ | |
|
||
//! IBC Relayer implementation | ||
|
||
pub mod chain; | ||
pub mod config; | ||
pub mod error; | ||
pub mod query; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
use tendermint::abci; | ||
use tendermint::rpc::{self, endpoint::abci_query::AbciQuery}; | ||
|
||
use relayer_modules::Height; | ||
|
||
use crate::chain::Chain; | ||
|
||
mod client_consensus_state; | ||
pub use client_consensus_state::*; | ||
|
||
pub trait IbcResponse: Sized {} | ||
|
||
pub trait IbcQuery { | ||
type Response: IbcResponse; | ||
|
||
fn path(&self) -> abci::Path; | ||
fn height(&self) -> Height; | ||
fn prove(&self) -> bool; | ||
fn data(&self) -> Vec<u8>; | ||
} | ||
|
||
pub async fn ibc_query<C, Q>(chain: &C, query: Q) -> Result<AbciQuery, rpc::Error> | ||
where | ||
C: Chain, | ||
Q: IbcQuery, | ||
{ | ||
let abci_response = chain | ||
.rpc_client() | ||
.abci_query( | ||
Some(query.path()), | ||
query.data(), | ||
Some(query.height().into()), | ||
query.prove(), | ||
) | ||
.await?; | ||
|
||
if !abci_response.code.is_ok() { | ||
todo!() // fail with response log | ||
} | ||
|
||
// Data from trusted node or subspace query doesn't need verification | ||
if !is_query_store_with_proof(&query.path()) { | ||
return Ok(abci_response); | ||
} | ||
|
||
// TODO: Verify proof and return response | ||
Ok(abci_response) | ||
} | ||
|
||
/// Whether or not this path requires proof verification. | ||
/// | ||
/// is_query_store_with_proofxpects a format like /<queryType>/<storeName>/<subpath>, | ||
/// where queryType must be "store" and subpath must be "key" to require a proof. | ||
fn is_query_store_with_proof(_path: &abci::Path) -> bool { | ||
todo!() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
use std::marker::PhantomData; | ||
|
||
use tendermint::abci; | ||
|
||
use relayer_modules::ics02_client::state::ConsensusState; | ||
use relayer_modules::ics23_commitment::{CommitmentPath, CommitmentProof}; | ||
use relayer_modules::ics24_host::client::ClientId; | ||
use relayer_modules::path::{ConsensusStatePath, Path}; | ||
use relayer_modules::Height; | ||
|
||
use super::{ibc_query, IbcQuery, IbcResponse}; | ||
use crate::chain::Chain; | ||
use crate::error; | ||
|
||
pub struct QueryClientConsensusState<CS> { | ||
pub height: Height, | ||
pub consensus_state_path: ConsensusStatePath, | ||
marker: PhantomData<CS>, | ||
} | ||
|
||
impl<CS> QueryClientConsensusState<CS> { | ||
pub fn new(height: Height, client_id: ClientId) -> Self { | ||
Self { | ||
height, | ||
consensus_state_path: ConsensusStatePath { client_id, height }, | ||
marker: PhantomData, | ||
} | ||
} | ||
} | ||
|
||
impl<CS> IbcQuery for QueryClientConsensusState<CS> | ||
where | ||
CS: ConsensusState, | ||
{ | ||
type Response = ConsensusStateResponse<CS>; | ||
|
||
fn path(&self) -> abci::Path { | ||
"/store/ibc/key".parse().unwrap() | ||
} | ||
|
||
fn height(&self) -> Height { | ||
self.height | ||
} | ||
|
||
fn prove(&self) -> bool { | ||
true | ||
} | ||
|
||
fn data(&self) -> Vec<u8> { | ||
self.consensus_state_path.to_key().into() | ||
} | ||
} | ||
|
||
pub struct ConsensusStateResponse<CS> { | ||
pub consensus_state: CS, | ||
pub proof: CommitmentProof, | ||
pub proof_path: CommitmentPath, | ||
pub proof_height: Height, | ||
} | ||
|
||
impl<CS> ConsensusStateResponse<CS> { | ||
pub fn new( | ||
client_id: ClientId, | ||
consensus_state: CS, | ||
abci_proof: abci::Proof, | ||
proof_height: Height, | ||
) -> Self { | ||
let proof = CommitmentProof::from_bytes(abci_proof.as_ref()); | ||
let proof_path = | ||
CommitmentPath::from_path(ConsensusStatePath::new(client_id, proof_height)); | ||
|
||
ConsensusStateResponse { | ||
consensus_state, | ||
proof, | ||
proof_path, | ||
proof_height, | ||
} | ||
} | ||
} | ||
|
||
impl<CS> IbcResponse for ConsensusStateResponse<CS> | ||
where | ||
CS: ConsensusState, | ||
{ | ||
// fn from_bytes(_bytes: &[u8]) -> Result<Self, rpc::Error> { | ||
// todo!() | ||
// } | ||
} | ||
|
||
pub async fn query_client_consensus_state<C>( | ||
chain: &C, | ||
client_id: ClientId, | ||
height: Height, | ||
) -> Result<ConsensusStateResponse<C::ConsensusState>, error::Error> | ||
where | ||
C: Chain, | ||
{ | ||
let query = QueryClientConsensusState::<C::ConsensusState>::new(height, client_id.clone()); | ||
|
||
let response = ibc_query(chain, query) | ||
.await | ||
.map_err(|e| error::Kind::Rpc.context(e))?; | ||
|
||
// FIXME: Handle case where there is no value | ||
let consensus_state = amino_unmarshal_binary_length_prefixed(&response.value.unwrap())?; | ||
|
||
Ok(ConsensusStateResponse::new( | ||
client_id, | ||
consensus_state, | ||
response.proof.unwrap(), // FIXME: Handle case where there is no proof | ||
response.height.into(), | ||
)) | ||
} | ||
|
||
fn amino_unmarshal_binary_length_prefixed<T>(_bytes: &[u8]) -> Result<T, error::Error> { | ||
todo!() | ||
} |