Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add commands to initialize light client for given chain and start verifying headers #26

Merged
merged 43 commits into from
Mar 20, 2020
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
41720f9
Add function to initialize lite client without trusted state
romac Mar 10, 2020
ff255e8
Make LightClient generic over chain type via a generic store
romac Mar 10, 2020
dc07271
Add stub to initialize the light client with trust options
romac Mar 10, 2020
bc34fc3
Use custom sum type for store heights
romac Mar 10, 2020
c2a6c8a
Rename LiteClient to LightClient
romac Mar 10, 2020
c2fc234
Add stub command `light init`
romac Mar 10, 2020
991cd02
Implement LightClient::init_with_node_trusted_state
romac Mar 11, 2020
cf3a2a9
Implement LightClient::init_with_trusted_state
romac Mar 11, 2020
1905e14
Refactor light client
romac Mar 11, 2020
9f8e0f8
Verify trusted state on light client initialization
romac Mar 12, 2020
f45fc07
Remove unused file
romac Mar 12, 2020
883362a
Add stub for Client::check_trusted_header
romac Mar 12, 2020
9e02e89
Fail when needed in Client::update_trusted_state
romac Mar 12, 2020
fb360ef
Partially implement Client::update
romac Mar 12, 2020
e1c93e3
Implement LightClient::verify_header
romac Mar 12, 2020
348168e
Update comment
romac Mar 12, 2020
023a695
Fix clippy warnings
romac Mar 12, 2020
13f9ab6
Use serde-humantime to parse trusting_period
romac Mar 12, 2020
afd6cd3
Move config defaults into their own module
romac Mar 12, 2020
0ea569c
Create light client and display last trusted state
romac Mar 12, 2020
b157162
Use checked arithmetic when incrementing height
romac Mar 12, 2020
272f681
Update trusted store in Client::update
romac Mar 12, 2020
3853042
Fix clippy warnings
romac Mar 12, 2020
179a844
Rename StoreHeight:GivenHeight to Given
romac Mar 13, 2020
39a9d94
Simplify verify_header signature
romac Mar 13, 2020
dc4bfc7
Spawn empty relayer, and one client per configured chain
romac Mar 16, 2020
cf78a1f
Update tendermint-rs repository
romac Mar 16, 2020
3578515
Remove dep on tendermint-rs/light_node by copying RpcRequester code over
romac Mar 16, 2020
d328d92
Improve reporting a bit
romac Mar 16, 2020
084f215
Fix RpcRequester unit test
romac Mar 16, 2020
0699ed7
Add persistent trusted store implementation
romac Mar 16, 2020
60e0066
Use persistent trusted store in commands
romac Mar 16, 2020
ba6235f
Ignore database folders in Git
romac Mar 16, 2020
ddeffb0
Fix clippy warnings
romac Mar 16, 2020
c47247d
Remove superfluous Tendermint type struct
romac Mar 17, 2020
180b253
Add some doc comments
romac Mar 17, 2020
e75b5a1
Document the relayer::client::Client struct
romac Mar 17, 2020
73ce4bf
More doc comments
romac Mar 17, 2020
d24f5fb
Ignore .db and .sh files in cli folder
romac Mar 19, 2020
0692408
Fix misleading doc comment
romac Mar 19, 2020
af7bf63
Update README and LICENSE file
romac Mar 20, 2020
602aa10
Remove verbose flag in README
romac Mar 20, 2020
ab13ede
Add status info to `light init` command
romac Mar 20, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk

# Ignore database folders
**/*.db/
2 changes: 1 addition & 1 deletion modules/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ default = ["paths-cosmos"]
paths-cosmos = []

[dependencies]
tendermint = { git = "https://github.com/interchainio/tendermint-rs.git" }
tendermint = { git = "https://github.com/informalsystems/tendermint-rs.git" }

anomaly = "0.2.0"
thiserror = "1.0.11"
Expand Down
8 changes: 0 additions & 8 deletions modules/src/ics02_client/client_type.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use super::error;
use anomaly::fail;

pub struct Tendermint;

/// Type of the consensus algorithm
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ClientType {
Expand All @@ -18,12 +16,6 @@ impl ClientType {
}
}

impl From<Tendermint> for ClientType {
fn from(_: Tendermint) -> Self {
Self::Tendermint
}
}

impl std::str::FromStr for ClientType {
type Err = error::Error;

Expand Down
3 changes: 3 additions & 0 deletions relayer/cli/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
/target
**/*.rs.bk

*.sh
*.db
3 changes: 3 additions & 0 deletions relayer/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ authors = [

[dependencies]
relayer = { path = "../relay" }
tendermint = { git = "https://github.com/informalsystems/tendermint-rs.git" }

anomaly = "0.2.0"
gumdrop = "0.7"
serde = { version = "1", features = ["serde_derive"] }
thiserror = "1"
abscissa_tokio = "0.5.1"
tokio = "0.2.13"

[dependencies.abscissa_core]
version = "0.5.2"
Expand Down
6 changes: 5 additions & 1 deletion relayer/cli/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ impl Application for CliApp {
/// beyond the default ones provided by the framework, this is the place
/// to do so.
fn register_components(&mut self, command: &Self::Cmd) -> Result<(), FrameworkError> {
let components = self.framework_components(command)?;
use abscissa_tokio::TokioComponent;

let mut components = self.framework_components(command)?;
components.push(Box::new(TokioComponent::new()?));

self.state.components.register(components)
}

Expand Down
13 changes: 9 additions & 4 deletions relayer/cli/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
//! application's configuration file.

mod config;
mod light;
mod start;
mod version;

use self::{config::ConfigCmd, start::StartCmd, version::VersionCmd};
use self::{config::ConfigCmd, light::LightCmd, start::StartCmd, version::VersionCmd};

use crate::config::Config;
use abscissa_core::{Command, Configurable, FrameworkError, Help, Options, Runnable};
Expand All @@ -25,6 +26,10 @@ pub enum CliCmd {
#[options(help = "get usage information")]
Help(Help<Self>),

/// The `version` subcommand
#[options(help = "display version information")]
Version(VersionCmd),

/// The `start` subcommand
#[options(help = "start the relayer")]
Start(StartCmd),
Expand All @@ -33,9 +38,9 @@ pub enum CliCmd {
#[options(help = "manipulate the relayer configuration")]
Config(ConfigCmd),

/// The `version` subcommand
#[options(help = "display version information")]
Version(VersionCmd),
/// The `light` subcommand
#[options(help = "basic functionality for managing the lite clients")]
Light(LightCmd),
}

/// This trait allows you to define how application configuration is loaded.
Expand Down
13 changes: 13 additions & 0 deletions relayer/cli/src/commands/light.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//! `light` subcommand

use abscissa_core::{Command, Options, Runnable};

mod init;

/// `light` subcommand
#[derive(Command, Debug, Options, Runnable)]
pub enum LightCmd {
/// The `light init` subcommand
#[options(help = "initiate a light client for a given chain")]
Init(init::InitCmd),
}
111 changes: 111 additions & 0 deletions relayer/cli/src/commands/light/init.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use std::future::Future;

// use crate::application::APPLICATION;
use crate::prelude::*;

use abscissa_core::{Command, Options, Runnable};

use tendermint::chain::Id as ChainId;
use tendermint::hash::Hash;
use tendermint::lite::Height;

use relayer::chain::tendermint::TendermintChain;
use relayer::client::trust_options::TrustOptions;
use relayer::config::{ChainConfig, Config};
use relayer::store::{sled::SledStore, Store};

#[derive(Command, Debug, Options)]
pub struct InitCmd {
#[options(free, help = "identifier of the chain to initialize light client for")]
chain_id: Option<ChainId>,

#[options(help = "trusted header hash", short = "x")]
hash: Option<Hash>,

#[options(help = "trusted header height", short = "h")]
height: Option<Height>,
}

#[derive(Clone, Debug)]
struct InitOptions {
/// identifier of chain to initialize light client for
chain_id: ChainId,

/// trusted header hash
trusted_hash: Hash,

/// trusted header height
trusted_height: Height,
}

impl InitCmd {
fn get_chain_config_and_options(
&self,
config: &Config,
) -> Result<(ChainConfig, InitOptions), String> {
match (&self.chain_id, &self.hash, self.height) {
(Some(chain_id), Some(trusted_hash), Some(trusted_height)) => {
let chain_config = config.chains.iter().find(|c| c.id == *chain_id);

match chain_config {
Some(chain_config) => {
let opts = InitOptions {
chain_id: *chain_id,
trusted_hash: *trusted_hash,
trusted_height,
};

Ok((chain_config.clone(), opts))
}
None => Err(format!("cannot find chain {} in config", chain_id)),
}
}

(None, _, _) => Err("missing chain identifier".to_string()),
(_, None, _) => Err("missing trusted hash".to_string()),
(_, _, None) => Err("missing trusted height".to_string()),
}
}
}

impl Runnable for InitCmd {
/// Initialize the light client for the given chain
fn run(&self) {
// FIXME: This just hangs and never runs the given future
// abscissa_tokio::run(&APPLICATION, ...).unwrap();

let config = app_config();

let (chain_config, opts) = match self.get_chain_config_and_options(&config) {
Err(err) => {
status_err!("invalid options: {}", err);
return;
}
Ok(result) => result,
};

block_on(async {
let trust_options = TrustOptions::new(
opts.trusted_hash,
opts.trusted_height,
chain_config.trusting_period,
Default::default(),
)
.unwrap();

let mut store: SledStore<TendermintChain> =
relayer::store::persistent(format!("store_{}.db", chain_config.id));

store.set_trust_options(trust_options).unwrap(); // FIXME: unwrap
});
}
}

fn block_on<F: Future>(future: F) -> F::Output {
tokio::runtime::Builder::new()
.basic_scheduler()
.enable_all()
.build()
.unwrap()
.block_on(future)
}
105 changes: 93 additions & 12 deletions relayer/cli/src/commands/start.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,105 @@
//! `start` subcommand
use std::future::Future;
use std::time::{Duration, SystemTime};

/// App-local prelude includes `app_reader()`/`app_writer()`/`app_config()`
/// accessors along with logging macros. Customize as you see fit.
// use crate::application::APPLICATION;
use crate::prelude::*;

use abscissa_core::{Command, Options, Runnable};

/// `start` subcommand
///
/// The `Options` proc macro generates an option parser based on the struct
/// definition, and is defined in the `gumdrop` crate. See their documentation
/// for a more comprehensive example:
///
/// <https://docs.rs/gumdrop/>
use tendermint::lite::types::Header;

use relayer::chain::tendermint::TendermintChain;
use relayer::chain::Chain;
use relayer::client::Client;
use relayer::config::ChainConfig;
use relayer::store::Store;

#[derive(Command, Debug, Options)]
pub struct StartCmd {}

impl Runnable for StartCmd {
/// Start the application.
fn run(&self) {
status_ok!("{}", "Quitting...");
let config = app_config().clone();

// FIXME: This just hangs and never runs the given future
// abscissa_tokio::run(&APPLICATION, ...).unwrap();

block_on(async {
for chain_config in config.chains {
status_info!(
"Relayer",
"Spawning light client for chain {}",
chain_config.id
);

let _handle = tokio::spawn(async move {
let client = create_client(chain_config).await;
let trusted_state = client.last_trusted_state().unwrap();

status_ok!(
client.chain().id(),
"Spawned new client now at trusted state: {} at height {}",
trusted_state.last_header().header().hash(),
trusted_state.last_header().header().height(),
);

update_headers(client).await;
});
}

start_relayer().await
})
}
}

async fn start_relayer() {
let mut interval = tokio::time::interval(Duration::from_secs(3));

loop {
status_info!("Relayer", "Relayer is running");

interval.tick().await;
}
}

async fn update_headers<C: Chain, S: Store<C>>(mut client: Client<C, S>) {
let mut interval = tokio::time::interval(Duration::from_secs(3));

loop {
let result = client.update(SystemTime::now()).await;

match result {
Ok(Some(trusted_state)) => status_ok!(
client.chain().id(),
"Updated to trusted state: {} at height {}",
trusted_state.header().hash(),
trusted_state.header().height()
),

Ok(None) => status_info!(client.chain().id(), "Ignoring update to a previous state"),
Err(err) => status_info!(client.chain().id(), "Error when updating headers: {}", err),
}

interval.tick().await;
}
}

async fn create_client(
chain_config: ChainConfig,
) -> Client<TendermintChain, impl Store<TendermintChain>> {
let chain = TendermintChain::from_config(chain_config).unwrap();

let store = relayer::store::persistent(format!("store_{}.db", chain.id()));
let trust_options = store.get_trust_options().unwrap(); // FIXME: unwrap

Client::new(chain, store, trust_options).await.unwrap()
}

fn block_on<F: Future>(future: F) -> F::Output {
tokio::runtime::Builder::new()
.basic_scheduler()
.enable_all()
.build()
.unwrap()
.block_on(future)
}
10 changes: 7 additions & 3 deletions relayer/relay/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,19 @@ authors = [
"Romain Ruetschi <romain@informal.systems>"
]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
relayer-modules = { path = "../../modules" }
tendermint = { git = "https://github.com/interchainio/tendermint-rs.git" }
tendermint = { git = "https://github.com/informalsystems/tendermint-rs.git" }

anomaly = "0.2.0"
humantime-serde = "1.0.0"
serde = "1.0.97"
serde_derive = "1.0"
thiserror = "1.0.11"
toml = "0.5"
async-trait = "0.1.24"
sled = "0.31.0"
serde_cbor = "0.11.1"

[dev-dependencies]
tokio = { version = "0.2.13", features = ["macros"] }
Loading