Skip to content

Commit

Permalink
Use a stateless light client (informalsystems#783)
Browse files Browse the repository at this point in the history
* Use stateless light client API

* Use peer id and timeout for light client config

* Update changelog

* Add LightClient::fetch method

* Remove LightClient::verify_to_latest

* Simply mock light client types

* Allow specifying a client state to derive light client security params for each request

* Fix mock light client

* Fix import name
  • Loading branch information
romac authored Apr 1, 2021
1 parent 8634265 commit 55bdc22
Show file tree
Hide file tree
Showing 12 changed files with 196 additions and 234 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
- Consistent identifier handling across ICS 02, 03 and 04 ([#622])

- [ibc-relayer]
- [nothing yet]
- Use a stateless light client without a runtime ([#673])

- [ibc-relayer-cli]
- Added `create connection` and `create channel` CLIs ([#630], [#715])
Expand Down Expand Up @@ -72,6 +72,7 @@
[#599]: https://github.com/informalsystems/ibc-rs/issues/599
[#630]: https://github.com/informalsystems/ibc-rs/issues/630
[#672]: https://github.com/informalsystems/ibc-rs/issues/672
[#673]: https://github.com/informalsystems/ibc-rs/issues/673
[#685]: https://github.com/informalsystems/ibc-rs/issues/685
[#689]: https://github.com/informalsystems/ibc-rs/issues/689
[#695]: https://github.com/informalsystems/ibc-rs/issues/695
Expand Down
4 changes: 1 addition & 3 deletions relayer/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,7 @@ pub trait Chain: Sized {

#[allow(clippy::type_complexity)]
/// Initializes and returns the light client (if any) associated with this chain.
fn init_light_client(
&self,
) -> Result<(Box<dyn LightClient<Self>>, Option<thread::JoinHandle<()>>), Error>;
fn init_light_client(&self) -> Result<Box<dyn LightClient<Self>>, Error>;

/// Initializes and returns the event monitor (if any) associated with this chain.
fn init_event_monitor(
Expand Down
15 changes: 4 additions & 11 deletions relayer/src/chain/cosmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,18 +335,11 @@ impl Chain for CosmosSdkChain {
})
}

// TODO use a simpler approach to create the light client
#[allow(clippy::type_complexity)]
fn init_light_client(
&self,
) -> Result<(Box<dyn LightClient<Self>>, Option<thread::JoinHandle<()>>), Error> {
fn init_light_client(&self) -> Result<Box<dyn LightClient<Self>>, Error> {
crate::time!("init_light_client");

let (lc, supervisor) = TMLightClient::from_config(&self.config, true)?;

let supervisor_thread = thread::spawn(move || supervisor.run().unwrap());

Ok((Box::new(lc), Some(supervisor_thread)))
let light_client = TMLightClient::from_config(&self.config)?;
Ok(Box::new(light_client))
}

fn init_event_monitor(
Expand Down Expand Up @@ -461,7 +454,7 @@ impl Chain for CosmosSdkChain {

if status.sync_info.catching_up {
fail!(
Kind::LightClientSupervisor(self.config.id.clone()),
Kind::LightClient(self.config.rpc_addr.to_string()),
"node at {} running chain {} not caught up",
self.config().rpc_addr,
self.config().id,
Expand Down
21 changes: 11 additions & 10 deletions relayer/src/chain/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,6 @@ pub enum ChainRequest {
reply_to: ReplyTo<Vec<IbcEvent>>,
},

GetMinimalSet {
from: Height,
to: Height,
reply_to: ReplyTo<Vec<AnyHeader>>,
},

Signer {
reply_to: ReplyTo<Signer>,
},
Expand All @@ -87,6 +81,7 @@ pub enum ChainRequest {
BuildHeader {
trusted_height: Height,
target_height: Height,
client_state: AnyClientState,
reply_to: ReplyTo<AnyHeader>,
},

Expand All @@ -96,7 +91,9 @@ pub enum ChainRequest {
},

BuildConsensusState {
height: Height,
trusted: Height,
target: Height,
client_state: AnyClientState,
reply_to: ReplyTo<AnyConsensusState>,
},

Expand Down Expand Up @@ -222,8 +219,6 @@ pub trait ChainHandle: DynClone + Send + Sync + Debug {
/// Send a transaction with `msgs` to chain.
fn send_msgs(&self, proto_msgs: Vec<prost_types::Any>) -> Result<Vec<IbcEvent>, Error>;

fn get_minimal_set(&self, from: Height, to: Height) -> Result<Vec<AnyHeader>, Error>;

fn get_signer(&self) -> Result<Signer, Error>;

fn get_key(&self) -> Result<KeyEntry, Error>;
Expand Down Expand Up @@ -293,13 +288,19 @@ pub trait ChainHandle: DynClone + Send + Sync + Debug {
&self,
trusted_height: Height,
target_height: Height,
client_state: AnyClientState,
) -> Result<AnyHeader, Error>;

/// Constructs a client state at the given height
fn build_client_state(&self, height: Height) -> Result<AnyClientState, Error>;

/// Constructs a consensus state at the given height
fn build_consensus_state(&self, height: Height) -> Result<AnyConsensusState, Error>;
fn build_consensus_state(
&self,
trusted: Height,
target: Height,
client_state: AnyClientState,
) -> Result<AnyConsensusState, Error>;

fn build_connection_proofs_and_client_state(
&self,
Expand Down
20 changes: 14 additions & 6 deletions relayer/src/chain/handle/prod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,6 @@ impl ChainHandle for ProdChainHandle {
})
}

fn get_minimal_set(&self, from: Height, to: Height) -> Result<Vec<AnyHeader>, Error> {
self.send(|reply_to| ChainRequest::GetMinimalSet { from, to, reply_to })
}

fn get_signer(&self) -> Result<Signer, Error> {
self.send(|reply_to| ChainRequest::Signer { reply_to })
}
Expand Down Expand Up @@ -214,10 +210,12 @@ impl ChainHandle for ProdChainHandle {
&self,
trusted_height: Height,
target_height: Height,
client_state: AnyClientState,
) -> Result<AnyHeader, Error> {
self.send(|reply_to| ChainRequest::BuildHeader {
trusted_height,
target_height,
client_state,
reply_to,
})
}
Expand All @@ -226,8 +224,18 @@ impl ChainHandle for ProdChainHandle {
self.send(|reply_to| ChainRequest::BuildClientState { height, reply_to })
}

fn build_consensus_state(&self, height: Height) -> Result<AnyConsensusState, Error> {
self.send(|reply_to| ChainRequest::BuildConsensusState { height, reply_to })
fn build_consensus_state(
&self,
trusted: Height,
target: Height,
client_state: AnyClientState,
) -> Result<AnyConsensusState, Error> {
self.send(|reply_to| ChainRequest::BuildConsensusState {
trusted,
target,
client_state,
reply_to,
})
}

fn build_connection_proofs_and_client_state(
Expand Down
9 changes: 2 additions & 7 deletions relayer/src/chain/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,8 @@ impl Chain for MockChain {
})
}

#[allow(clippy::type_complexity)]
fn init_light_client(
&self,
) -> Result<(Box<dyn LightClient<Self>>, Option<thread::JoinHandle<()>>), Error> {
let light_client = MockLightClient::new(self);

Ok((Box::new(light_client), None))
fn init_light_client(&self) -> Result<Box<dyn LightClient<Self>>, Error> {
Ok(Box::new(MockLightClient::new(self)))
}

fn init_event_monitor(
Expand Down
57 changes: 23 additions & 34 deletions relayer/src/chain/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ use ibc::ics02_client::client_consensus::AnyConsensusState;
use ibc::ics02_client::client_state::AnyClientState;

pub struct Threads {
pub light_client: Option<thread::JoinHandle<()>>,
pub chain_runtime: thread::JoinHandle<()>,
pub event_monitor: Option<thread::JoinHandle<()>>,
}
Expand Down Expand Up @@ -84,16 +83,15 @@ impl<C: Chain + Send + 'static> ChainRuntime<C> {
let chain = C::bootstrap(config, rt.clone())?;

// Start the light client
let (light_client_handler, light_client_thread) = chain.init_light_client()?;
let light_client = chain.init_light_client()?;

// Start the event monitor
let (event_receiver, event_monitor_thread) = chain.init_event_monitor(rt.clone())?;

// Instantiate & spawn the runtime
let (handle, runtime_thread) = Self::init(chain, light_client_handler, event_receiver, rt);
let (handle, runtime_thread) = Self::init(chain, light_client, event_receiver, rt);

let threads = Threads {
light_client: light_client_thread,
chain_runtime: runtime_thread,
event_monitor: event_monitor_thread,
};
Expand Down Expand Up @@ -173,10 +171,6 @@ impl<C: Chain + Send + 'static> ChainRuntime<C> {
self.send_msgs(proto_msgs, reply_to)?
},

Ok(ChainRequest::GetMinimalSet { from, to, reply_to }) => {
self.get_minimal_set(from, to, reply_to)?
}

Ok(ChainRequest::Signer { reply_to }) => {
self.get_signer(reply_to)?
}
Expand All @@ -189,16 +183,16 @@ impl<C: Chain + Send + 'static> ChainRuntime<C> {
self.module_version(port_id, reply_to)?
}

Ok(ChainRequest::BuildHeader { trusted_height, target_height, reply_to }) => {
self.build_header(trusted_height, target_height, reply_to)?
Ok(ChainRequest::BuildHeader { trusted_height, target_height, client_state, reply_to }) => {
self.build_header(trusted_height, target_height, client_state, reply_to)?
}

Ok(ChainRequest::BuildClientState { height, reply_to }) => {
self.build_client_state(height, reply_to)?
}

Ok(ChainRequest::BuildConsensusState { height, reply_to }) => {
self.build_consensus_state(height, reply_to)?
Ok(ChainRequest::BuildConsensusState { trusted, target, client_state, reply_to }) => {
self.build_consensus_state(trusted, target, client_state, reply_to)?
}

Ok(ChainRequest::BuildConnectionProofsAndClientState { message_type, connection_id, client_id, height, reply_to }) => {
Expand Down Expand Up @@ -324,15 +318,6 @@ impl<C: Chain + Send + 'static> ChainRuntime<C> {
Ok(())
}

fn get_minimal_set(
&self,
_from: Height,
_to: Height,
_reply_to: ReplyTo<Vec<AnyHeader>>,
) -> Result<(), Error> {
todo!()
}

fn get_signer(&mut self, reply_to: ReplyTo<Signer>) -> Result<(), Error> {
let result = self.chain.get_signer();

Expand Down Expand Up @@ -364,23 +349,25 @@ impl<C: Chain + Send + 'static> ChainRuntime<C> {
}

fn build_header(
&self,
&mut self,
trusted_height: Height,
target_height: Height,
client_state: AnyClientState,
reply_to: ReplyTo<AnyHeader>,
) -> Result<(), Error> {
let header = {
// Get the light block at trusted_height + 1 from chain.
// TODO - This is tendermint specific and needs to be refactored during
// the relayer light client refactoring.
// Note: This is needed to get the next validator set. While there is a next validator set
// in the light block at trusted height, the proposer is not known/set in this set.
let trusted_light_block = self
.light_client
.verify_to_target(trusted_height.increment())?;
//
// TODO: This is tendermint specific and needs to be refactored during
// the relayer light client refactoring.
// NOTE: This is needed to get the next validator set. While there is a next validator set
// in the light block at trusted height, the proposer is not known/set in this set.
let trusted_light_block = self.light_client.fetch(trusted_height.increment())?;

// Get the light block at target_height from chain.
let target_light_block = self.light_client.verify_to_target(target_height)?;
let target_light_block =
self.light_client
.verify(trusted_height, target_height, &client_state)?;

let header =
self.chain
Expand Down Expand Up @@ -416,15 +403,17 @@ impl<C: Chain + Send + 'static> ChainRuntime<C> {

/// Constructs a consensus state for the given height
fn build_consensus_state(
&self,
height: Height,
&mut self,
trusted: Height,
target: Height,
client_state: AnyClientState,
reply_to: ReplyTo<AnyConsensusState>,
) -> Result<(), Error> {
let latest_light_block = self.light_client.verify_to_target(height)?;
let light_block = self.light_client.verify(trusted, target, &client_state)?;

let consensus_state = self
.chain
.build_consensus_state(latest_light_block)
.build_consensus_state(light_block)
.map(|cs| cs.wrap_any());

reply_to
Expand Down
19 changes: 12 additions & 7 deletions relayer/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
use anomaly::{BoxError, Context};
use thiserror::Error;

use ibc::ics24_host::identifier::{ChainId, ChannelId, ConnectionId};
use ibc::{
ics02_client::client_type::ClientType,
ics24_host::identifier::{ChannelId, ConnectionId},
};

/// An error that can be raised by the relayer.
pub type Error = anomaly::Error<Kind>;
Expand Down Expand Up @@ -35,13 +38,9 @@ pub enum Kind {
#[error("GRPC error")]
Grpc,

/// Light client supervisor error
#[error("Light client supervisor error for chain id {0}")]
LightClientSupervisor(ChainId),

/// Light client instance error, typically raised by a `Client`
#[error("Light client instance error for rpc address {0}")]
LightClientInstance(String),
#[error("Light client error for RPC address {0}")]
LightClient(String),

/// Trusted store error, raised by instances of `Store`
#[error("Store error")]
Expand Down Expand Up @@ -169,6 +168,12 @@ pub enum Kind {

#[error("bech32 encoding failed")]
Bech32Encoding(#[from] bech32::Error),

#[error("client type mismatch: expected '{expected}', got '{got}'")]
ClientTypeMismatch {
expected: ClientType,
got: ClientType,
},
}

impl Kind {
Expand Down
13 changes: 7 additions & 6 deletions relayer/src/foreign_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ impl ForeignClient {
.wrap_any();

let consensus_state = self.src_chain
.build_consensus_state(latest_height)
.build_consensus_state(client_state.latest_height(), latest_height, client_state.clone())
.map_err(|e| ForeignClientError::ClientCreate(format!("failed while building client consensus state from src chain ({}) with error: {}", self.src_chain.id(), e)))?
.wrap_any();

Expand Down Expand Up @@ -312,17 +312,18 @@ impl ForeignClient {
thread::sleep(Duration::from_millis(100))
}

// Get the latest trusted height from the client state on destination.
let trusted_height = self
// Get the latest client state on destination.
let client_state = self
.dst_chain()
.query_client_state(&self.id, Height::default())
.map_err(|e| {
ForeignClientError::ClientUpdate(format!(
"failed querying client state on dst chain {} with error: {}",
self.id, e
))
})?
.latest_height();
})?;

let trusted_height = client_state.latest_height();

if trusted_height >= target_height {
warn!(
Expand All @@ -334,7 +335,7 @@ impl ForeignClient {

let header = self
.src_chain()
.build_header(trusted_height, target_height)
.build_header(trusted_height, target_height, client_state)
.map_err(|e| {
ForeignClientError::ClientUpdate(format!(
"failed building header with error: {}",
Expand Down
Loading

0 comments on commit 55bdc22

Please sign in to comment.