diff --git a/CHANGELOG.md b/CHANGELOG.md index dd7ee92a5d..2ac51f1ed1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ - Added `client-upgrade` CLI ([#357]) - Update gaia to version 4.1.0 for e2e tests on CI ([#702]) - Add `start-multi` command to relay on all paths defined in the configuration ([#748]) + - Add option to specify which events to listen for in `listen` command ([#550]) ### IMPROVEMENTS @@ -56,21 +57,26 @@ - Hermes guide: improved installation guideline ([#672]) - Make fee denom and amount configurable ([#754]) +- [ibc-proto] + - Fix for proto files re-compilation bug ([#801]) + ### BREAKING CHANGES - [ibc] - `MsgConnectionOpenAck.counterparty_connection_id` is now a `ConnectionId` instead of an `Option`([#700]) - [ibc-relayer] - - [nothing yet] + - Remove the light client configuration from the global configuration ([#793]) - [ibc-relayer-cli] - - [nothing yet] + - Remove the light add and light rm commands ([#793]) + [#352]: https://github.com/informalsystems/ibc-rs/issues/352 [#357]: https://github.com/informalsystems/ibc-rs/issues/357 [#416]: https://github.com/informalsystems/ibc-rs/issues/416 [#561]: https://github.com/informalsystems/ibc-rs/issues/561 +[#550]: https://github.com/informalsystems/ibc-rs/issues/550 [#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 @@ -91,7 +97,9 @@ [#761]: https://github.com/informalsystems/ibc-rs/issues/761 [#772]: https://github.com/informalsystems/ibc-rs/issues/772 [#770]: https://github.com/informalsystems/ibc-rs/issues/770 +[#793]: https://github.com/informalsystems/ibc-rs/pull/793 [#798]: https://github.com/informalsystems/ibc-rs/issues/798 +[#801]: https://github.com/informalsystems/ibc-rs/issues/801 ## v0.1.1 diff --git a/ci/e2e.sh b/ci/e2e.sh index aff85b07af..6e9c037f05 100755 --- a/ci/e2e.sh +++ b/ci/e2e.sh @@ -33,30 +33,6 @@ echo "Add keys for chains" echo "-----------------------------------------------------------------------------------------------------------------" hermes -c "$CONFIG_PATH" keys add "$CHAIN_A" key_seed_"$CHAIN_A".json hermes -c "$CONFIG_PATH" keys add "$CHAIN_B" key_seed_"$CHAIN_B".json -echo "-----------------------------------------------------------------------------------------------------------------" -echo "Set the primary peers for clients on each chain " -echo "-----------------------------------------------------------------------------------------------------------------" -LIGHT_ADD_CHAIN_A="hermes -c $CONFIG_PATH light add http://$CHAIN_A:$CHAIN_A_PORT -c $CHAIN_A -s $CHAIN_A_HOME -p -y -f" -echo "Executing: $LIGHT_ADD_CHAIN_A" -bash -c "$LIGHT_ADD_CHAIN_A" -sleep 2 -echo "-----------------------------------------------------------------------------------------------------------------" -LIGHT_ADD_CHAIN_B="hermes -c $CONFIG_PATH light add http://$CHAIN_B:$CHAIN_B_PORT -c $CHAIN_B -s $CHAIN_B_HOME -p -y -f" -echo "Executing: $LIGHT_ADD_CHAIN_B" -bash -c "$LIGHT_ADD_CHAIN_B" -sleep 2 -echo "-----------------------------------------------------------------------------------------------------------------" -echo "Set the secondary peers for clients on each chain " -echo "-----------------------------------------------------------------------------------------------------------------" -LIGHT_ADD_CHAIN_A_PEER="hermes -c $CONFIG_PATH light add http://$CHAIN_A:$CHAIN_A_PORT -c $CHAIN_A -s $CHAIN_A_HOME --peer-id 17D46D8C1576A79203A6733F63B2C9B7235DD559 -y" -echo "Executing: $LIGHT_ADD_CHAIN_A_PEER" -bash -c "$LIGHT_ADD_CHAIN_A_PEER" -sleep 2 -echo "-----------------------------------------------------------------------------------------------------------------" -LIGHT_ADD_CHAIN_B_PEER="hermes -c $CONFIG_PATH light add http://$CHAIN_B:$CHAIN_B_PORT -c $CHAIN_B -s $CHAIN_B_HOME --peer-id A885BB3D3DFF6101188B462466AE926E7A6CD51E -y" -echo "Executing: $LIGHT_ADD_CHAIN_B_PEER" -bash -c "$LIGHT_ADD_CHAIN_B_PEER" -sleep 2 echo "=================================================================================================================" echo " END-TO-END TESTS " diff --git a/ci/simple_config.toml b/ci/simple_config.toml index 7aaf2aff1d..35be457c28 100644 --- a/ci/simple_config.toml +++ b/ci/simple_config.toml @@ -1,5 +1,4 @@ [global] -timeout = '10s' strategy = 'naive' log_level = 'info' @@ -8,6 +7,7 @@ id = 'ibc-0' rpc_addr = 'http://ibc-0:26657' grpc_addr = 'http://ibc-0:9090' websocket_addr = 'ws://ibc-0:26657/websocket' +rpc_timeout = '10s' account_prefix = 'cosmos' key_name = 'testkey' store_prefix = 'ibc' @@ -22,6 +22,7 @@ id = 'ibc-1' rpc_addr = 'http://ibc-1:26657' grpc_addr = 'http://ibc-1:9090' websocket_addr = 'ws://ibc-1:26657/websocket' +rpc_timeout = '10s' account_prefix = 'cosmos' key_name = 'testkey' store_prefix = 'ibc' diff --git a/config.toml b/config.toml index fc677da33d..2904ee82d5 100644 --- a/config.toml +++ b/config.toml @@ -1,5 +1,4 @@ [global] -timeout = '10s' strategy = 'naive' log_level = 'error' @@ -8,6 +7,7 @@ id = 'ibc-0' rpc_addr = 'http://127.0.0.1:26657' grpc_addr = 'http://127.0.0.1:9090' websocket_addr = 'ws://127.0.0.1:26657/websocket' +rpc_timeout = '10s' account_prefix = 'cosmos' key_name = 'testkey' store_prefix = 'ibc' @@ -26,6 +26,7 @@ id = 'ibc-1' rpc_addr = 'http://127.0.0.1:26557' grpc_addr = 'http://127.0.0.1:9091' websocket_addr = 'ws://127.0.0.1:26557/websocket' +rpc_timeout = '10s' account_prefix = 'cosmos' key_name = 'testkey' store_prefix = 'ibc' diff --git a/config_example.toml b/config_example.toml index 0685f82317..b335c8a178 100644 --- a/config_example.toml +++ b/config_example.toml @@ -1,13 +1,13 @@ [global] -timeout = '10s' strategy = 'naive' log_level = 'error' [[chains]] id = 'ibc-0' -rpc_addr = 'tcp://localhost:26657' -grpc_addr = 'tcp://localhost:9090' +rpc_addr = 'http://localhost:26657' +grpc_addr = 'http://localhost:9090' websocket_addr = 'ws://localhost:26657/websocket' +rpc_timeout = '10s' account_prefix = 'cosmos' key_name = 'testkey' store_prefix = 'ibc' @@ -23,9 +23,10 @@ denominator = '3' [[chains]] id = 'ibc-1' -rpc_addr = 'tcp://localhost:26557' -grpc_addr = 'tcp://localhost:9091' +rpc_addr = 'http://localhost:26557' +grpc_addr = 'http://localhost:9091' websocket_addr = 'ws://localhost:26557/websocket' +rpc_timeout = '10s' account_prefix = 'cosmos' key_name = 'testkey' store_prefix = 'ibc' diff --git a/docs/architecture/adr-002-ibc-relayer.md b/docs/architecture/adr-002-ibc-relayer.md index 487a211fbb..1df200c548 100644 --- a/docs/architecture/adr-002-ibc-relayer.md +++ b/docs/architecture/adr-002-ibc-relayer.md @@ -2,6 +2,7 @@ ## Changelog * 2020-05-19: First draft. Accepted +* 2020-04-06: Configuration updates ## Definitions These definitions are specific for this document and they may not be consistent with the IBC Specification. @@ -110,20 +111,23 @@ The relayer MAY: The initial implementation will heavily borrow from the Go relayer implementation that uses a "naive" algorithm for relaying messages. The structure of the configuration file is similar with the one in Go (see [Go-Relayer](https://github.com/cosmos/relayer)) ### Configuration -(WIP) + +> WIP + Upon start the relayer reads a configuration file that includes global and per chain parameters. The file format is .toml Below is an example of a configuration file. ```toml -title = "IBC Relayer Config Example" - [global] -timeout = "10s" strategy = "naive" +log_level = "error" [[chains]] id = "chain_A" - rpc_addr = "localhost:26657" + rpc_addr = "http://localhost:26657" + grpc_addr = "http://localhost:9090" + websocket_addr = "ws://localhost:26657/websocket" + rpc_timeout = "10s" account_prefix = "cosmos" key_name = "testkey" store_prefix = "ibc" @@ -135,7 +139,10 @@ strategy = "naive" [[chains]] id = "chain_B" - rpc_addr = "localhost:26557" + rpc_addr = "http://localhost:26557" + grpc_addr = "http://localhost:9091" + websocket_addr = "ws://localhost:26557/websocket" + rpc_timeout = "10s" account_prefix = "cosmos" key_name = "testkey" store_prefix = "ibc" @@ -197,13 +204,19 @@ pub enum Strategy { } pub struct GlobalConfig { - pub timeout: Duration, pub strategy: Strategy, + + /// All valid log levels, as defined in tracing: + /// https://docs.rs/tracing-core/0.1.17/tracing_core/struct.Level.html + pub log_level: String, } pub struct ChainConfig { pub id: ChainId, pub rpc_addr: tendermint_rpc::Url, + pub websocket_addr: tendermint_rpc::Url, + pub grpc_addr: tendermint_rpc::Url, + pub rpc_timeout: Duration, pub account_prefix: String, pub key_name: String, pub client_ids: Vec, @@ -248,13 +261,6 @@ To validate a configuration file: The command verifies that the specified configuration file parses and it is semantically correct. -#### Light Client Initialization -To initialize a light client: - -`relayer -c light init -x -h ` - -The command initializes the light client for `` with a trusted height and hash. This should be done for all chains for which relaying is performed. - #### Start To start the relayer: diff --git a/docs/architecture/adr-006-hermes-v0.2-usecases.md b/docs/architecture/adr-006-hermes-v0.2-usecases.md index 4620f33760..ee52cd728e 100644 --- a/docs/architecture/adr-006-hermes-v0.2-usecases.md +++ b/docs/architecture/adr-006-hermes-v0.2-usecases.md @@ -230,7 +230,6 @@ of the config file will look as follows: ```toml [global] -timeout = '10s' strategy = 'naive' log_level = 'error' log_json = 'false' diff --git a/proto-compiler/README.md b/proto-compiler/README.md index 0ea210c722..967fd3905d 100644 --- a/proto-compiler/README.md +++ b/proto-compiler/README.md @@ -23,7 +23,7 @@ Note: the full commit hash must be specified. Alternatively, one can check out a tag for the Cosmos SDK with the `--sdk-tag` option: ```bash -$ cargo run -- clone --out /tmp/cosmos --sdk-tag v0.42.1 --ibc-commit 333c1f338b2a14a1928a6f8ab64c37123c0e97b6 +$ cargo run -- clone --out /tmp/cosmos --sdk-tag v0.42.1 --ibc-go-commit 333c1f338b2a14a1928a6f8ab64c37123c0e97b6 ``` ### Generate Rust sources from Protobuf definitions @@ -34,6 +34,8 @@ To generate the Rust sources from the Protobuf definitions, and copy them to the $ cargo run -- compile --sdk /tmp/cosmos/sdk --ibc /tmp/cosmos/ibc --out ../proto/src/prost ``` -Additionally, this command will output the commit hash at which the Cosmos SDK is checked out into `$out/COSMOS_SDK_COMMIT`. +Additionally, this command will output the commit hash at which the Cosmos SDK is checked out into `$out/COSMOS_SDK_COMMIT` and +similarly the commit hash for IBC-go is saved into `$out/COSMOS_IBC_VERSION`. -This value is exposed via the `ibc_proto::COSMOS_SDK_VERSION` constant in the `ibc-proto` library. +The two commit values are exposed via the `ibc_proto::COSMOS_SDK_VERSION` and `ibc_proto::COSMOS_IBC_VERSION` +constants in the `ibc-proto` library. diff --git a/proto-compiler/src/cmd/clone.rs b/proto-compiler/src/cmd/clone.rs index 4cfe6e4352..1808a71388 100644 --- a/proto-compiler/src/cmd/clone.rs +++ b/proto-compiler/src/cmd/clone.rs @@ -25,6 +25,9 @@ pub struct CloneCmd { out: PathBuf, } +pub const COSMOS_SDK_URL: &str = "https://github.com/cosmos/cosmos-sdk"; +pub const IBC_GO_URL: &str = "https://github.com/cosmos/ibc-go"; + impl CloneCmd { pub fn validate(&self) { if self.sdk_commit.is_some() && self.sdk_tag.is_some() { @@ -48,27 +51,26 @@ impl CloneCmd { pub fn run(&self) { self.validate(); - let sdk_repo = if self.out.exists() { + let sdk_path = self.sdk_subdir(); + let sdk_repo = if sdk_path.exists() { println!( - "[info ] Found Cosmos SDK or IBC proto source at '{}'", - self.out.display() + "[info ] Found Cosmos SDK source at '{}'", + sdk_path.display() ); - Repository::open(&self.out).unwrap_or_else(|e| { + Repository::open(&sdk_path).unwrap_or_else(|e| { println!("[error] Failed to open repository: {}", e); process::exit(1) }) } else { println!("[info ] Cloning cosmos/cosmos-sdk repository..."); - let url = "https://github.com/cosmos/cosmos-sdk"; - - let repo = Repository::clone(url, &self.sdk_subdir()).unwrap_or_else(|e| { + let repo = Repository::clone(COSMOS_SDK_URL, &sdk_path).unwrap_or_else(|e| { println!("[error] Failed to clone the SDK repository: {}", e); process::exit(1) }); - println!("[info ] Cloned at '{}'", self.sdk_subdir().display()); + println!("[info ] Cloned at '{}'", sdk_path.display()); repo }; @@ -87,17 +89,25 @@ impl CloneCmd { println!("[info ] Cloning cosmos/ibc-go repository..."); - let ibc_url = "https://github.com/cosmos/ibc-go"; + let ibc_path = self.ibc_subdir(); + let ibc_repo = if ibc_path.exists() { + println!("[info ] Found IBC Go source at '{}'", sdk_path.display()); - let ibc_repo = Repository::clone(ibc_url, &self.ibc_subdir()).unwrap_or_else(|e| { - println!("[error] Failed to clone the IBC repository: {}", e); - process::exit(1) - }); + Repository::open(&sdk_path).unwrap_or_else(|e| { + println!("[error] Failed to open repository: {}", e); + process::exit(1) + }) + } else { + Repository::clone(IBC_GO_URL, &ibc_path).unwrap_or_else(|e| { + println!("[error] Failed to clone the IBC Go repository: {}", e); + process::exit(1) + }) + }; - println!("[info ] Cloned at '{}'", self.ibc_subdir().display()); + println!("[info ] Cloned at '{}'", ibc_path.display()); checkout_commit(&ibc_repo, &self.ibc_go_commit).unwrap_or_else(|e| { println!( - "[error] Failed to checkout IBC commit {}: {}", + "[error] Failed to checkout IBC Go commit {}: {}", self.ibc_go_commit, e ); process::exit(1) diff --git a/proto-compiler/src/cmd/compile.rs b/proto-compiler/src/cmd/compile.rs index 733ee8b5ee..92b389f4f4 100644 --- a/proto-compiler/src/cmd/compile.rs +++ b/proto-compiler/src/cmd/compile.rs @@ -27,13 +27,16 @@ pub struct CompileCmd { impl CompileCmd { pub fn run(&self) { - let tmp = TempDir::new("ibc-proto").unwrap(); + let tmp_sdk = TempDir::new("ibc-proto-sdk").unwrap(); + Self::output_version(&self.sdk, tmp_sdk.as_ref(), "COSMOS_SDK_COMMIT"); + Self::compile_sdk_protos(&self.sdk, tmp_sdk.as_ref()); - Self::output_version(&self.sdk, tmp.as_ref(), "COSMOS_SDK_COMMIT"); - Self::output_version(&self.ibc, tmp.as_ref(), "COSMOS_IBC_COMMIT"); - Self::compile_sdk_protos(&self.sdk, tmp.as_ref()); - Self::compile_ibc_protos(&self.ibc, &self.sdk, tmp.as_ref()); - Self::copy_generated_files(tmp.as_ref(), &self.out); + let tmp_ibc = TempDir::new("ibc-proto-ibc-go").unwrap(); + Self::output_version(&self.ibc, tmp_ibc.as_ref(), "COSMOS_IBC_COMMIT"); + Self::compile_ibc_protos(&self.ibc, tmp_ibc.as_ref()); + + // Merge the generated files into a single directory, taking care not to overwrite anything + Self::copy_generated_files(tmp_sdk.as_ref(), tmp_ibc.as_ref(), &self.out); } fn output_version(dir: &Path, out_dir: &Path, commit_file: &str) { @@ -45,7 +48,7 @@ impl CompileCmd { std::fs::write(path, rev).unwrap(); } - fn compile_ibc_protos(ibc_dir: &Path, sdk_dir: &Path, out_dir: &Path) { + fn compile_ibc_protos(ibc_dir: &Path, out_dir: &Path) { println!( "[info ] Compiling IBC .proto files to Rust into '{}'...", out_dir.display() @@ -59,7 +62,6 @@ impl CompileCmd { let proto_includes_paths = [ format!("{}/proto", ibc_dir.display()), - format!("{}/proto/cosmos", sdk_dir.display()), format!("{}/third_party/proto", ibc_dir.display()), ]; @@ -182,7 +184,7 @@ impl CompileCmd { } } - fn copy_generated_files(from_dir: &Path, to_dir: &Path) { + fn copy_generated_files(from_dir_sdk: &Path, from_dir_ibc: &Path, to_dir: &Path) { println!( "[info ] Copying generated files into '{}'...", to_dir.display() @@ -193,7 +195,8 @@ impl CompileCmd { create_dir_all(&to_dir).unwrap(); // Copy new compiled files (prost does not use folder structures) - let errors = WalkDir::new(from_dir) + // Copy the SDK files first + let errors_sdk = WalkDir::new(from_dir_sdk) .into_iter() .filter_map(|e| e.ok()) .filter(|e| e.file_type().is_file()) @@ -210,9 +213,53 @@ impl CompileCmd { .filter_map(|e| e.err()) .collect::>(); - if !errors.is_empty() { - for e in errors { - println!("[error] Error while copying compiled file: {}", e); + if !errors_sdk.is_empty() { + for e in errors_sdk { + println!("[error] Error while copying SDK-compiled file: {}", e); + } + + panic!("[error] Aborted."); + } + + // Copy the IBC-go files second, double-checking if anything is overwritten + let errors_ibc = WalkDir::new(from_dir_ibc) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| e.file_type().is_file()) + .map(|e| { + let generated_fname = e.file_name().to_owned().into_string().unwrap(); + let prefix = &generated_fname[0..6]; + + let target_fname = format!( + "{}/{}", + to_dir.display(), + generated_fname, + ); + + // If it's a cosmos-relevant file and it exists, we should not overwrite it. + if Path::new(&target_fname).exists() && prefix.eq("cosmos") { + let original_cosmos_file = std::fs::read(target_fname.clone()).unwrap(); + let new_cosmos_file = std::fs::read(e.path()).unwrap(); + if original_cosmos_file != new_cosmos_file { + println!( + "[warn ] Cosmos-related file exists already {}! Ignoring the one generated from IBC-go {:?}", + target_fname, e.path() + ); + } + Ok(0) + } else { + copy( + e.path(), + target_fname, + ) + } + }) + .filter_map(|e| e.err()) + .collect::>(); + + if !errors_ibc.is_empty() { + for e in errors_ibc { + println!("[error] Error while copying IBC-go compiled file: {}", e); } panic!("[error] Aborted."); diff --git a/relayer-cli/src/cli_utils.rs b/relayer-cli/src/cli_utils.rs index 2bb48c0b80..51c8f3fa42 100644 --- a/relayer-cli/src/cli_utils.rs +++ b/relayer-cli/src/cli_utils.rs @@ -1,7 +1,6 @@ use ibc::ics24_host::identifier::ChainId; use ibc_relayer::chain::runtime::ChainRuntime; use ibc_relayer::chain::CosmosSdkChain; -use ibc_relayer::config::{ChainConfig, StoreConfig}; use ibc_relayer::{chain::handle::ChainHandle, config::Config}; use crate::error::{Error, Kind}; @@ -23,20 +22,8 @@ impl ChainHandlePair { src_chain_id: &ChainId, dst_chain_id: &ChainId, ) -> Result { - Self::spawn_with(Default::default(), config, src_chain_id, dst_chain_id) - } - - /// Spawn the source and destination chain runtime from the configuration and chain identifiers, - /// and return the pair of associated handles. Accepts a `SpawnOptions` argument, which - /// is used to override each chain configuration before spawning its runtime. - pub fn spawn_with( - options: SpawnOptions, - config: &Config, - src_chain_id: &ChainId, - dst_chain_id: &ChainId, - ) -> Result { - let src = spawn_chain_runtime(options.clone(), config, src_chain_id)?; - let dst = spawn_chain_runtime(options, config, dst_chain_id)?; + let src = spawn_chain_runtime(config, src_chain_id)?; + let dst = spawn_chain_runtime(config, dst_chain_id)?; Ok(ChainHandlePair { src, dst }) } @@ -45,18 +32,15 @@ impl ChainHandlePair { /// Spawns a chain runtime from the configuration and given a chain identifier. /// Returns the corresponding handle if successful. pub fn spawn_chain_runtime( - spawn_options: SpawnOptions, config: &Config, chain_id: &ChainId, ) -> Result, Error> { - let mut chain_config = config + let chain_config = config .find_chain(chain_id) .cloned() .ok_or_else(|| format!("missing chain for id ({}) in configuration file", chain_id)) .map_err(|e| Kind::Config.context(e))?; - spawn_options.apply(&mut chain_config); - let chain_res = ChainRuntime::::spawn(chain_config).map_err(|e| Kind::Runtime.context(e)); @@ -64,38 +48,3 @@ pub fn spawn_chain_runtime( Ok(handle) } - -/// Allows override the chain configuration just before -/// spawning a new runtime instance. -/// -/// This is currently only used to override the configured -/// light client store for one-off commands which do not -/// need the disk-based store. -#[derive(Clone, Debug, Default)] -pub struct SpawnOptions { - override_store_config: Option, -} - -impl SpawnOptions { - /// Override the light client store config with the provided config. - pub fn override_store_config(store_config: StoreConfig) -> Self { - Self { - override_store_config: Some(store_config), - } - } - - /// Apply these options to the provided chain configuration. - pub fn apply(&self, chain_config: &mut ChainConfig) { - if let Some(store_config) = &self.override_store_config { - Self::apply_store_config(chain_config, &store_config) - } - } - - fn apply_store_config(chain_config: &mut ChainConfig, store_config: &StoreConfig) { - if let Some(peer_config) = chain_config.peers.as_mut() { - for light_client in &mut peer_config.light_clients { - light_client.store = store_config.clone(); - } - } - } -} diff --git a/relayer-cli/src/commands.rs b/relayer-cli/src/commands.rs index 811f468120..d9f495633a 100644 --- a/relayer-cli/src/commands.rs +++ b/relayer-cli/src/commands.rs @@ -12,15 +12,13 @@ use abscissa_core::{Command, Configurable, FrameworkError, Help, Options, Runnab use crate::config::Config; use self::{ - create::CreateCmds, keys::KeysCmd, light::LightCmd, listen::ListenCmd, query::QueryCmd, - start::StartCmd, start_multi::StartMultiCmd, tx::TxCmd, update::UpdateCmds, - version::VersionCmd, + create::CreateCmds, keys::KeysCmd, listen::ListenCmd, query::QueryCmd, start::StartCmd, + start_multi::StartMultiCmd, tx::TxCmd, update::UpdateCmds, version::VersionCmd, }; mod config; mod create; mod keys; -mod light; mod listen; mod query; mod start; @@ -50,10 +48,6 @@ pub enum CliCmd { #[options(help = "Manage keys in the relayer for each chain")] Keys(KeysCmd), - /// The `light` subcommand - #[options(help = "Basic functionality for managing the light clients")] - Light(LightCmd), - /// The `create` subcommand #[options(help = "Create objects (client, connection, or channel) on chains")] Create(CreateCmds), diff --git a/relayer-cli/src/commands/create/channel.rs b/relayer-cli/src/commands/create/channel.rs index 82a3c7869b..df94458afb 100644 --- a/relayer-cli/src/commands/create/channel.rs +++ b/relayer-cli/src/commands/create/channel.rs @@ -6,11 +6,10 @@ use ibc::ics04_channel::channel::Order; use ibc::ics24_host::identifier::{ChainId, ConnectionId, PortId}; use ibc::Height; use ibc_relayer::channel::Channel; -use ibc_relayer::config::StoreConfig; use ibc_relayer::connection::Connection; use ibc_relayer::foreign_client::ForeignClient; -use crate::cli_utils::{spawn_chain_runtime, ChainHandlePair, SpawnOptions}; +use crate::cli_utils::{spawn_chain_runtime, ChainHandlePair}; use crate::conclude::{exit_with_unrecoverable_error, Output}; use crate::prelude::*; @@ -69,11 +68,9 @@ impl CreateChannelCommand { fn run_using_new_connection(&self, chain_b_id: &ChainId) { let config = app_config(); - let spawn_options = SpawnOptions::override_store_config(StoreConfig::memory()); + let chains = ChainHandlePair::spawn(&config, &self.chain_a_id, chain_b_id) + .unwrap_or_else(exit_with_unrecoverable_error); - let chains = - ChainHandlePair::spawn_with(spawn_options, &config, &self.chain_a_id, chain_b_id) - .unwrap_or_else(exit_with_unrecoverable_error); info!( "Creating new clients, new connection, and a new channel with order {:?} and version {}", self.order, self.version @@ -100,10 +97,8 @@ impl CreateChannelCommand { fn run_reusing_connection(&self) { let config = app_config(); - let spawn_options = SpawnOptions::override_store_config(StoreConfig::memory()); - // Validate & spawn runtime for side a. - let chain_a = spawn_chain_runtime(spawn_options.clone(), &config, &self.chain_a_id) + let chain_a = spawn_chain_runtime(&config, &self.chain_a_id) .unwrap_or_else(exit_with_unrecoverable_error); // Unwrap the identifier of the connection on side a. @@ -130,8 +125,8 @@ impl CreateChannelCommand { .unwrap_or_else(exit_with_unrecoverable_error); // Spawn the runtime for side b. - let chain_b = spawn_chain_runtime(spawn_options, &config, &chain_b_id) - .unwrap_or_else(exit_with_unrecoverable_error); + let chain_b = + spawn_chain_runtime(&config, &chain_b_id).unwrap_or_else(exit_with_unrecoverable_error); // Create the foreign client handles. let client_a = ForeignClient::find(chain_b.clone(), chain_a.clone(), conn_end.client_id()) diff --git a/relayer-cli/src/commands/create/connection.rs b/relayer-cli/src/commands/create/connection.rs index e7cca54b2c..4fd4019ae8 100644 --- a/relayer-cli/src/commands/create/connection.rs +++ b/relayer-cli/src/commands/create/connection.rs @@ -3,11 +3,10 @@ use abscissa_core::{Command, Options, Runnable}; use ibc::ics02_client::client_state::ClientState; use ibc::ics24_host::identifier::{ChainId, ClientId}; use ibc::Height; -use ibc_relayer::config::StoreConfig; use ibc_relayer::connection::Connection; use ibc_relayer::foreign_client::ForeignClient; -use crate::cli_utils::{spawn_chain_runtime, ChainHandlePair, SpawnOptions}; +use crate::cli_utils::{spawn_chain_runtime, ChainHandlePair}; use crate::conclude::{exit_with_unrecoverable_error, Output}; use crate::prelude::*; @@ -60,11 +59,8 @@ impl CreateConnectionCommand { fn run_using_new_clients(&self, chain_b_id: &ChainId) { let config = app_config(); - let spawn_options = SpawnOptions::override_store_config(StoreConfig::memory()); - - let chains = - ChainHandlePair::spawn_with(spawn_options, &config, &self.chain_a_id, chain_b_id) - .unwrap_or_else(exit_with_unrecoverable_error); + let chains = ChainHandlePair::spawn(&config, &self.chain_a_id, chain_b_id) + .unwrap_or_else(exit_with_unrecoverable_error); // Validate the other options. Bail if the CLI was invoked with incompatible options. if self.client_a.is_some() { @@ -99,9 +95,8 @@ impl CreateConnectionCommand { fn run_reusing_clients(&self) { let config = app_config(); - let spawn_options = SpawnOptions::override_store_config(StoreConfig::memory()); // Validate & spawn runtime for chain_a. - let chain_a = match spawn_chain_runtime(spawn_options.clone(), &config, &self.chain_a_id) { + let chain_a = match spawn_chain_runtime(&config, &self.chain_a_id) { Ok(handle) => handle, Err(e) => return Output::error(format!("{}", e)).exit(), }; @@ -131,7 +126,7 @@ impl CreateConnectionCommand { }; // Validate & spawn runtime for chain_b. - let chain_b = match spawn_chain_runtime(spawn_options, &config, &chain_b_id) { + let chain_b = match spawn_chain_runtime(&config, &chain_b_id) { Ok(handle) => handle, Err(e) => return Output::error(format!("{}", e)).exit(), }; diff --git a/relayer-cli/src/commands/light.rs b/relayer-cli/src/commands/light.rs deleted file mode 100644 index ca92b73e8d..0000000000 --- a/relayer-cli/src/commands/light.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! `light` subcommand - -use abscissa_core::{Command, Options, Runnable}; - -mod add; -mod rm; - -/// `light` subcommand -#[derive(Command, Debug, Options, Runnable)] -pub enum LightCmd { - /// The `light add` subcommand - #[options(help = "Add a light client peer for a given chain")] - Add(add::AddCmd), - - /// The `light rm` subcommand - #[options(help = "Remove a light client peer for a given chain")] - Rm(rm::RmCmd), -} diff --git a/relayer-cli/src/commands/light/add.rs b/relayer-cli/src/commands/light/add.rs deleted file mode 100644 index 245128d3ae..0000000000 --- a/relayer-cli/src/commands/light/add.rs +++ /dev/null @@ -1,287 +0,0 @@ -use std::{fmt, io, io::Write, ops::Deref, path::PathBuf}; - -use abscissa_core::{application::fatal_error, error::BoxError, Command, Options, Runnable}; - -use config::StoreConfig; -use ibc::ics24_host::identifier::ChainId; -use ibc_relayer::{ - config, - config::{Config, LightClientConfig, PeersConfig}, - util::block_on, -}; -use tendermint::block::Height; -use tendermint::hash::Hash; -use tendermint_light_client::types::PeerId; -use tendermint_rpc::{Client, HttpClient}; - -use crate::prelude::*; - -#[derive(Command, Debug, Options)] -pub struct AddCmd { - /// RPC network address (required) - #[options(free)] - address: Option, - - /// identifier of the chain (required) - #[options(short = "c")] - chain_id: Option, - - /// path to light client store for this peer (required) - store: Option, - - /// whether this is the primary peer - primary: bool, - - /// allow overriding an existing peer - force: bool, - - /// skip confirmation - yes: bool, - - /// override peer id (optional) - #[options(no_short)] - peer_id: Option, - - /// override height (optional) - #[options(no_short)] - height: Option, - - /// override hash (optional) - #[options(no_short)] - hash: Option, -} - -impl AddCmd { - fn cmd(&self) -> Result<(), BoxError> { - let config = (*app_config()).clone(); - let options = AddOptions::from_cmd(self).map_err(|e| format!("invalid options: {}", e))?; - - options - .validate() - .map_err(|e| format!("invalid options: {}", e))?; - - add(config, options) - } -} - -impl Runnable for AddCmd { - fn run(&self) { - self.cmd() - .unwrap_or_else(|e| fatal_error(app_reader().deref(), &*e)) - } -} - -#[derive(Clone, Debug)] -struct AddOptions { - /// identifier of the chain - chain_id: ChainId, - - /// RPC network address - address: tendermint_rpc::Url, - - /// whether this is the primary peer or not - primary: bool, - - /// path to light client store for this peer - store: PathBuf, - - /// override peer id - override_peer_id: Option, - - /// override height - override_height: Option, - - /// override hash - override_hash: Option, - - /// allow overriding an existing peer - force: bool, - - /// skip confirmation - yes: bool, -} - -impl AddOptions { - fn from_cmd(cmd: &AddCmd) -> Result { - let chain_id = cmd.chain_id.clone().ok_or("missing chain identifier")?; - let address = cmd.address.clone().ok_or("missing RPC network address")?; - let store_path = cmd.store.clone().ok_or("missing store path")?; - - Ok(AddOptions { - chain_id, - address, - store: store_path, - override_peer_id: cmd.peer_id, - override_height: cmd.height, - override_hash: cmd.hash, - primary: cmd.primary, - force: cmd.force, - yes: cmd.yes, - }) - } - - fn validate(&self) -> Result<(), BoxError> { - if !self.store.exists() { - return Err(format!("Store path '{}' does not exists", self.store.display()).into()); - } - - if !self.store.is_dir() { - return Err(format!("Store path '{}' is not a directory", self.store.display()).into()); - } - - Ok(()) - } -} - -#[derive(Debug, Clone)] -pub struct NodeStatus { - chain_id: ChainId, - address: tendermint_rpc::Url, - peer_id: PeerId, - hash: Hash, - height: Height, -} - -impl fmt::Display for NodeStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, " chain id: {}", self.chain_id)?; - writeln!(f, " address: {}", self.address)?; - writeln!(f, " peer id: {}", self.peer_id)?; - writeln!(f, " height: {}", self.height)?; - writeln!(f, " hash: {}", self.hash)?; - - Ok(()) - } -} - -fn add(mut config: Config, options: AddOptions) -> Result<(), BoxError> { - // Fetch the status from the node - let mut status = fetch_status(options.chain_id.clone(), options.address.clone())?; - - // Override the fetched status with command line arguments, if given - override_status(&mut status, &options); - - // Ask the user for confirmation if --yes was not supplied - if !(options.yes || confirm(&status, options.primary)?) { - return Ok(()); - } - - // Update the in-memory configuration - let new_primary = update_config(options, status.clone(), &mut config)?; - - // Write the updated configuration to disk - let config_path = crate::config::config_path()?; - ibc_relayer::config::store(&config, config_path)?; - - status_ok!( - "Success", - "Added light client:\n{} primary: {}", - status, - status.peer_id == new_primary, - ); - - Ok(()) -} - -fn fetch_status(chain_id: ChainId, address: tendermint_rpc::Url) -> Result { - let rpc_client = HttpClient::new(address.clone())?; - let response = block_on(rpc_client.status())?; - - let peer_id = response.node_info.id; - let height = response.sync_info.latest_block_height; - let hash = response.sync_info.latest_block_hash; - - Ok(NodeStatus { - chain_id, - address, - peer_id, - hash, - height, - }) -} - -fn override_status(status: &mut NodeStatus, options: &AddOptions) { - if let Some(peer_id) = options.override_peer_id { - status.peer_id = peer_id; - } - - if let Some(height) = options.override_height { - status.height = height; - } - - if let Some(hash) = options.override_hash { - status.hash = hash; - } -} - -fn confirm(status: &NodeStatus, primary: bool) -> Result { - print!("Light client configuration:\n{}", status); - println!(" primary: {}", primary); - - loop { - print!("\n? Do you want to add a new light client with these trust options? (y/n) > "); - io::stdout().flush()?; // need to flush stdout since stdout is often line-buffered - - let mut choice = String::new(); - io::stdin().read_line(&mut choice)?; - - match choice.trim_end() { - "y" | "yes" => return Ok(true), - "n" | "no" => return Ok(false), - _ => continue, - } - } -} - -fn update_config( - options: AddOptions, - status: NodeStatus, - config: &mut Config, -) -> Result { - let chain_config = config - .find_chain_mut(&options.chain_id) - .ok_or_else(|| format!("could not find config for chain: {}", options.chain_id))?; - - let peers_config = chain_config.peers.get_or_insert_with(|| PeersConfig { - primary: status.peer_id, - light_clients: vec![], - }); - - // Check if the given peer exists already, in which case throw an error except if the - // --force flag is set. - let peer_exists = peers_config.light_client(status.peer_id).is_some(); - if peer_exists && !options.force { - return Err(format!( - "a peer with id {} already exists, remove it first \ - or pass the --force flag to override it", - status.peer_id - ) - .into()); - } - - let light_client_config = LightClientConfig { - peer_id: status.peer_id, - address: status.address.clone(), - timeout: config::default::timeout(), - trusted_header_hash: status.hash, - trusted_height: status.height, - store: StoreConfig::Disk { - path: options.store.join(status.peer_id.to_string()), - }, - }; - - if peer_exists { - // Filter out the light client config with the specified peer id - peers_config - .light_clients - .retain(|p| p.peer_id != status.peer_id); - } - - peers_config.light_clients.push(light_client_config); - - if options.primary { - peers_config.primary = status.peer_id; - } - - Ok(peers_config.primary) -} diff --git a/relayer-cli/src/commands/light/rm.rs b/relayer-cli/src/commands/light/rm.rs deleted file mode 100644 index dc2bbf8628..0000000000 --- a/relayer-cli/src/commands/light/rm.rs +++ /dev/null @@ -1,173 +0,0 @@ -use std::{io, io::Write, ops::Deref}; - -use abscissa_core::{application::fatal_error, error::BoxError, Command, Options, Runnable}; -use tendermint_light_client::types::PeerId; - -use ibc::ics24_host::identifier::ChainId; -use ibc_relayer::config::PeersConfig; - -use crate::conclude::Output; -use crate::prelude::*; - -#[derive(Command, Debug, Options)] -pub struct RmCmd { - /// identifiers of peers to remove - #[options(free)] - peer_id: Vec, - - /// identifier of the chain to remove peers from - #[options(short = "c")] - chain_id: Option, - - /// force removal of primary peer - #[options(short = "f")] - force: bool, - - /// remove all peers, implies --force - #[options(no_short)] - all: bool, - - /// skip confirmation - yes: bool, -} - -#[derive(Clone, Debug)] -struct RmOptions { - /// identifier of the chain to remove peers from - chain_id: ChainId, - - /// identifiers of peers to remove - peer_ids: Vec, - - /// remove all peers, implies --force - all: bool, - - /// force removal of primary peer - force: bool, - - /// skip confirmation - yes: bool, -} - -impl RmOptions { - fn from_cmd(cmd: &RmCmd) -> Result { - let chain_id = cmd.chain_id.clone().ok_or("missing chain identifier")?; - let peer_ids = if !cmd.all && cmd.peer_id.is_empty() { - return Err("missing peer identifier".into()); - } else { - cmd.peer_id.clone() - }; - - Ok(RmOptions { - chain_id, - peer_ids, - all: cmd.all, - yes: cmd.yes, - force: cmd.force, - }) - } -} - -impl Runnable for RmCmd { - fn run(&self) { - self.cmd() - .unwrap_or_else(|e| fatal_error(app_reader().deref(), &*e)) - } -} - -impl RmCmd { - fn cmd(&self) -> Result<(), BoxError> { - let options = RmOptions::from_cmd(self).map_err(|e| format!("invalid options: {}", e))?; - let mut config = (*app_config()).clone(); - - let chain_config = config - .find_chain_mut(&options.chain_id) - .ok_or_else(|| format!("could not find config for chain: {}", options.chain_id))?; - - let mut peers_config = chain_config - .peers - .as_mut() - .ok_or_else(|| format!("no peers configured for chain: {}", options.chain_id))?; - - let rmd_peers = if options.all && (options.yes || confirm(&options.chain_id)?) { - let removed_peers = get_all_peer_ids(&peers_config); - chain_config.peers = None; - - removed_peers - } else { - let mut res: Vec = vec![]; - for peer_id in options.peer_ids { - let removed_peer = remove_peer(&mut peers_config, peer_id, options.force)?; - res.push(removed_peer.to_string()); - } - - res - }; - - let config_path = crate::config::config_path()?; - ibc_relayer::config::store(&config, config_path)?; - - Output::success(format!("Removed light client peer(s) '{:?}'", rmd_peers)).exit(); - - Ok(()) - } -} - -fn remove_peer( - peers_config: &mut PeersConfig, - peer_id: PeerId, - force: bool, -) -> Result { - // Check if the given peer actually exists already, if not throw an error. - let peer_exists = peers_config.light_client(peer_id).is_some(); - if !peer_exists { - return Err(format!("cannot find peer: {}", peer_id).into()); - } - - // Only allow remove the primary peer if the --force option is set - let removed_primary = peers_config.primary == peer_id; - if removed_primary && !force { - return Err("cannot remove primary peer, pass --force flag to force removal".into()); - } - - // Filter out the light client config with the specified peer id - peers_config.light_clients.retain(|p| p.peer_id != peer_id); - - // If the peer we removed was the primary peer, use the next available peer as the primary, - // if any. - if removed_primary { - if let Some(new_primary) = peers_config.light_clients.first() { - peers_config.primary = new_primary.peer_id; - } - } - - Ok(peer_id) -} - -fn get_all_peer_ids(peers_config: &PeersConfig) -> Vec { - peers_config - .light_clients - .iter() - .map(|c| c.peer_id.to_string()) - .collect() -} - -fn confirm(chain_id: &ChainId) -> Result { - loop { - print!( - "\n? Do you really want to remove all peers for chain '{}'? (y/n) > ", - chain_id - ); - - io::stdout().flush()?; // need to flush stdout since stdout is often line-buffered - - let mut choice = String::new(); - io::stdin().read_line(&mut choice)?; - - match choice.trim_end() { - "y" | "yes" => return Ok(true), - "n" | "no" => return Ok(false), - _ => continue, - } - } -} diff --git a/relayer-cli/src/commands/listen.rs b/relayer-cli/src/commands/listen.rs index 26070e55db..96400f0e8b 100644 --- a/relayer-cli/src/commands/listen.rs +++ b/relayer-cli/src/commands/listen.rs @@ -2,8 +2,11 @@ use std::{ops::Deref, sync::Arc, thread}; use abscissa_core::{application::fatal_error, error::BoxError, Command, Options, Runnable}; use crossbeam_channel as channel; +use itertools::Itertools; use tokio::runtime::Runtime as TokioRuntime; +use tendermint_rpc::query::{EventType, Query}; + use ibc::ics24_host::identifier::ChainId; use ibc_relayer::{config::ChainConfig, event::monitor::*}; @@ -11,19 +14,30 @@ use crate::prelude::*; #[derive(Command, Debug, Options)] pub struct ListenCmd { + /// Identifier of the chain to listen for events from #[options(free)] - chain_id: Option, + chain_id: ChainId, + + /// Add an event type to listen for, can be repeated. Listen for all events by default (available: Tx, NewBlock) + #[options(short = "e", long = "event", meta = "EVENT")] + events: Vec, } impl ListenCmd { fn cmd(&self) -> Result<(), BoxError> { - let rt = Arc::new(TokioRuntime::new()?); let config = app_config(); - let chain_id = self.chain_id.clone().unwrap(); - let chain_config = config.find_chain(&chain_id).unwrap(); + let chain_config = config + .find_chain(&self.chain_id) + .ok_or_else(|| format!("chain '{}' not found in configuration", self.chain_id))?; + + let events = if self.events.is_empty() { + &[EventType::Tx, EventType::NewBlock] + } else { + self.events.as_slice() + }; - listen(rt, chain_config.clone()) + listen(chain_config, events) } } @@ -35,11 +49,18 @@ impl Runnable for ListenCmd { } /// Listen to events -pub fn listen(rt: Arc, config: ChainConfig) -> Result<(), BoxError> { - info!(chain.id = %config.id, "spawning event monitor for"); +pub fn listen(config: &ChainConfig, events: &[EventType]) -> Result<(), BoxError> { + println!( + "[info] Listening for events `{}` on '{}'...", + events.iter().format(", "), + config.id + ); - let (event_monitor, rx) = subscribe(config, rt)?; - let _ = thread::spawn(|| event_monitor.run()); + let rt = Arc::new(TokioRuntime::new()?); + let queries = events.iter().cloned().map(Query::from).collect(); + let (event_monitor, rx) = subscribe(&config, queries, rt)?; + + thread::spawn(|| event_monitor.run()); while let Ok(event_batch) = rx.recv() { dbg!(event_batch); @@ -49,16 +70,22 @@ pub fn listen(rt: Arc, config: ChainConfig) -> Result<(), BoxError } fn subscribe( - chain_config: ChainConfig, + chain_config: &ChainConfig, + queries: Vec, rt: Arc, ) -> Result<(EventMonitor, channel::Receiver), BoxError> { - let (mut event_monitor, rx) = - EventMonitor::new(chain_config.id, chain_config.websocket_addr, rt) - .map_err(|e| format!("couldn't initialize event monitor: {}", e))?; + let (mut event_monitor, rx) = EventMonitor::new( + chain_config.id.clone(), + chain_config.websocket_addr.clone(), + rt, + ) + .map_err(|e| format!("could not initialize event monitor: {}", e))?; + + event_monitor.set_queries(queries); event_monitor .subscribe() - .map_err(|e| format!("couldn't initialize subscriptions: {}", e))?; + .map_err(|e| format!("could not initialize subscriptions: {}", e))?; Ok((event_monitor, rx)) } diff --git a/relayer-cli/src/commands/tx/channel.rs b/relayer-cli/src/commands/tx/channel.rs index 0931c2b06f..914f78c52c 100644 --- a/relayer-cli/src/commands/tx/channel.rs +++ b/relayer-cli/src/commands/tx/channel.rs @@ -6,9 +6,8 @@ use ibc::ics04_channel::channel::Order; use ibc::ics24_host::identifier::{ChainId, ChannelId, ClientId, ConnectionId, PortId}; use ibc::Height; use ibc_relayer::channel::{Channel, ChannelSide}; -use ibc_relayer::config::StoreConfig; -use crate::cli_utils::{ChainHandlePair, SpawnOptions}; +use crate::cli_utils::ChainHandlePair; use crate::conclude::Output; use crate::error::{Error, Kind}; use crate::prelude::*; @@ -17,13 +16,8 @@ macro_rules! tx_chan_cmd { ($dbg_string:literal, $func:ident, $self:expr, $chan:expr) => { let config = app_config(); - let spawn_options = SpawnOptions::override_store_config(StoreConfig::memory()); - let chains = match ChainHandlePair::spawn_with( - spawn_options, - &config, - &$self.src_chain_id, - &$self.dst_chain_id, - ) { + let chains = match ChainHandlePair::spawn(&config, &$self.src_chain_id, &$self.dst_chain_id) + { Ok(chains) => chains, Err(e) => return Output::error(format!("{}", e)).exit(), }; diff --git a/relayer-cli/src/commands/tx/client.rs b/relayer-cli/src/commands/tx/client.rs index 38b0b3a209..8f6701a978 100644 --- a/relayer-cli/src/commands/tx/client.rs +++ b/relayer-cli/src/commands/tx/client.rs @@ -3,11 +3,10 @@ use tracing::info; use ibc::events::IbcEvent; use ibc::ics24_host::identifier::{ChainId, ClientId}; -use ibc_relayer::config::StoreConfig; use ibc_relayer::foreign_client::ForeignClient; use crate::application::app_config; -use crate::cli_utils::{ChainHandlePair, SpawnOptions}; +use crate::cli_utils::ChainHandlePair; use crate::conclude::{exit_with_unrecoverable_error, Output}; use crate::error::{Error, Kind}; @@ -26,13 +25,7 @@ impl Runnable for TxCreateClientCmd { fn run(&self) { let config = app_config(); - let spawn_options = SpawnOptions::override_store_config(StoreConfig::memory()); - let chains = match ChainHandlePair::spawn_with( - spawn_options, - &config, - &self.src_chain_id, - &self.dst_chain_id, - ) { + let chains = match ChainHandlePair::spawn(&config, &self.src_chain_id, &self.dst_chain_id) { Ok(chains) => chains, Err(e) => return Output::error(format!("{}", e)).exit(), }; @@ -75,13 +68,7 @@ impl Runnable for TxUpdateClientCmd { fn run(&self) { let config = app_config(); - let spawn_options = SpawnOptions::override_store_config(StoreConfig::memory()); - let chains = match ChainHandlePair::spawn_with( - spawn_options, - &config, - &self.src_chain_id, - &self.dst_chain_id, - ) { + let chains = match ChainHandlePair::spawn(&config, &self.src_chain_id, &self.dst_chain_id) { Ok(chains) => chains, Err(e) => return Output::error(format!("{}", e)).exit(), }; @@ -127,14 +114,8 @@ impl Runnable for TxUpgradeClientCmd { fn run(&self) { let config = app_config(); - let spawn_options = SpawnOptions::override_store_config(StoreConfig::memory()); - let chains = ChainHandlePair::spawn_with( - spawn_options, - &config, - &self.src_chain_id, - &self.dst_chain_id, - ) - .unwrap_or_else(exit_with_unrecoverable_error); + let chains = ChainHandlePair::spawn(&config, &self.src_chain_id, &self.dst_chain_id) + .unwrap_or_else(exit_with_unrecoverable_error); info!("Started the chain runtimes"); diff --git a/relayer-cli/src/commands/tx/connection.rs b/relayer-cli/src/commands/tx/connection.rs index 702ff36906..161416cf32 100644 --- a/relayer-cli/src/commands/tx/connection.rs +++ b/relayer-cli/src/commands/tx/connection.rs @@ -2,10 +2,9 @@ use abscissa_core::{Command, Options, Runnable}; use ibc::events::IbcEvent; use ibc::ics24_host::identifier::{ChainId, ClientId, ConnectionId}; -use ibc_relayer::config::StoreConfig; use ibc_relayer::connection::{Connection, ConnectionSide}; -use crate::cli_utils::{ChainHandlePair, SpawnOptions}; +use crate::cli_utils::ChainHandlePair; use crate::conclude::Output; use crate::error::{Error, Kind}; use crate::prelude::*; @@ -14,13 +13,8 @@ macro_rules! conn_open_cmd { ($dbg_string:literal, $func:ident, $self:expr, $conn:expr) => { let config = app_config(); - let spawn_options = SpawnOptions::override_store_config(StoreConfig::memory()); - let chains = match ChainHandlePair::spawn_with( - spawn_options, - &config, - &$self.src_chain_id, - &$self.dst_chain_id, - ) { + let chains = match ChainHandlePair::spawn(&config, &$self.src_chain_id, &$self.dst_chain_id) + { Ok(chains) => chains, Err(e) => return Output::error(format!("{}", e)).exit(), }; diff --git a/relayer-cli/src/commands/tx/packet.rs b/relayer-cli/src/commands/tx/packet.rs index bf765a6df9..d31ad2e0be 100644 --- a/relayer-cli/src/commands/tx/packet.rs +++ b/relayer-cli/src/commands/tx/packet.rs @@ -2,10 +2,9 @@ use abscissa_core::{Command, Options, Runnable}; use ibc::events::IbcEvent; use ibc::ics24_host::identifier::{ChainId, ChannelId, PortId}; -use ibc_relayer::config::StoreConfig; use ibc_relayer::link::{Link, LinkParameters}; -use crate::cli_utils::{ChainHandlePair, SpawnOptions}; +use crate::cli_utils::ChainHandlePair; use crate::conclude::Output; use crate::error::{Error, Kind}; use crate::prelude::*; @@ -29,13 +28,7 @@ impl Runnable for TxRawPacketRecvCmd { fn run(&self) { let config = app_config(); - let spawn_options = SpawnOptions::override_store_config(StoreConfig::memory()); - let chains = match ChainHandlePair::spawn_with( - spawn_options, - &config, - &self.src_chain_id, - &self.dst_chain_id, - ) { + let chains = match ChainHandlePair::spawn(&config, &self.src_chain_id, &self.dst_chain_id) { Ok(chains) => chains, Err(e) => return Output::error(format!("{}", e)).exit(), }; @@ -79,13 +72,7 @@ impl Runnable for TxRawPacketAckCmd { fn run(&self) { let config = app_config(); - let spawn_options = SpawnOptions::override_store_config(StoreConfig::memory()); - let chains = match ChainHandlePair::spawn_with( - spawn_options, - &config, - &self.src_chain_id, - &self.dst_chain_id, - ) { + let chains = match ChainHandlePair::spawn(&config, &self.src_chain_id, &self.dst_chain_id) { Ok(chains) => chains, Err(e) => return Output::error(format!("{}", e)).exit(), }; diff --git a/relayer-cli/src/registry.rs b/relayer-cli/src/registry.rs index eaf8a8f4d0..5f0b395448 100644 --- a/relayer-cli/src/registry.rs +++ b/relayer-cli/src/registry.rs @@ -30,7 +30,7 @@ impl<'a> Registry<'a> { /// return its handle. pub fn get_or_spawn(&mut self, chain_id: &ChainId) -> Result, Error> { if !self.handles.contains_key(chain_id) { - let handle = spawn_chain_runtime(Default::default(), &self.config, chain_id)?; + let handle = spawn_chain_runtime(&self.config, chain_id)?; self.handles.insert(chain_id.clone(), handle); } diff --git a/relayer-cli/tests/fixtures/two_chains.toml b/relayer-cli/tests/fixtures/two_chains.toml index d73a2dc06e..0e4548b42a 100644 --- a/relayer-cli/tests/fixtures/two_chains.toml +++ b/relayer-cli/tests/fixtures/two_chains.toml @@ -1,5 +1,4 @@ [global] -timeout = '10s' strategy = 'naive' log_level = 'error' # valid options: 'error', 'warn', 'info', 'debug', 'trace' @@ -8,14 +7,11 @@ id = 'ibc-0' rpc_addr = 'http://127.0.0.1:26657' grpc_addr = 'http://127.0.0.1:9090' websocket_addr = 'ws://127.0.0.1:26657/websocket' +rpc_timeout = '10s' account_prefix = 'cosmos' key_name = 'testkey' store_prefix = 'ibc' - -client_ids = [ - 'cla1', - 'cla2', -] +client_ids = ['cla1', 'cla2'] gas = 200000 clock_drift = '5s' trusting_period = '14days' @@ -24,23 +20,12 @@ trusting_period = '14days' numerator = '1' denominator = '3' -[chains.peers] -primary = 'E0DE6C540C65FC9059660A7A8535F70048A94539' - -[[chains.peers.light_clients]] -peer_id = 'E0DE6C540C65FC9059660A7A8535F70048A94539' -address = 'http://127.0.0.1:26657' -trusted_header_hash = '169F8F6318B8FAABDBA128AD1689E238566B69DDBD2B36F1911C0DFCA73FD401' -trusted_height = '7806' - -[chains.peers.light_clients.store] -type = 'memory' - [[chains]] id = 'ibc-1' rpc_addr = 'http://127.0.0.1:26657' grpc_addr = 'http://127.0.0.1:9090' websocket_addr = 'ws://127.0.0.1:26657/websocket' +rpc_timeout = '10s' account_prefix = 'cosmos' key_name = 'testkey' store_prefix = 'ibc' @@ -52,15 +37,3 @@ trusting_period = '14days' [chains.trust_threshold] numerator = '1' denominator = '3' - -[chains.peers] -primary = '356DB878F398BA707DBE69950067C8E6471D8948' - -[[chains.peers.light_clients]] -peer_id = '356DB878F398BA707DBE69950067C8E6471D8948' -address = 'http://127.0.0.1:26557' -trusted_header_hash = 'CC291E79B2E2068984EB13BBF420B4F4AE95357E25B9BA0A30CA26FF27AF3C75' -trusted_height = '7741' - -[chains.peers.light_clients.store] -type = 'memory' diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index dce44693fd..8c50a11598 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -62,7 +62,7 @@ use crate::config::ChainConfig; use crate::error::{Error, Kind}; use crate::event::monitor::{EventBatch, EventMonitor}; use crate::keyring::store::{KeyEntry, KeyRing, KeyRingOperations, StoreBackend}; -use crate::light_client::tendermint::LightClient as TMLightClient; +use crate::light_client::tendermint::LightClient as TmLightClient; use crate::light_client::LightClient; use super::Chain; @@ -320,7 +320,7 @@ impl Chain for CosmosSdkChain { .map_err(|e| Kind::Rpc(config.rpc_addr.clone()).context(e))?; // Initialize key store and load key - let key_store = KeyRing::init(StoreBackend::Test, config.clone()) + let keybase = KeyRing::init(StoreBackend::Test, config.clone()) .map_err(|e| Kind::KeyBase.context(e))?; let grpc_addr = @@ -329,16 +329,25 @@ impl Chain for CosmosSdkChain { Ok(Self { rt, config, - keybase: key_store, + keybase, rpc_client, grpc_addr, }) } fn init_light_client(&self) -> Result>, Error> { + use tendermint_light_client::types::PeerId; + crate::time!("init_light_client"); - let light_client = TMLightClient::from_config(&self.config)?; + let peer_id: PeerId = self + .rt + .block_on(self.rpc_client.status()) + .map(|s| s.node_info.id) + .map_err(|e| Kind::Rpc(self.config.rpc_addr.clone()).context(e))?; + + let light_client = TmLightClient::from_config(&self.config, peer_id)?; + Ok(Box::new(light_client)) } diff --git a/relayer/src/chain/mock.rs b/relayer/src/chain/mock.rs index ee9bd48d24..7653ee290d 100644 --- a/relayer/src/chain/mock.rs +++ b/relayer/src/chain/mock.rs @@ -339,6 +339,7 @@ pub mod test_utils { rpc_addr: "http://127.0.0.1:26656".parse().unwrap(), grpc_addr: "http://127.0.0.1:9090".parse().unwrap(), websocket_addr: "ws://127.0.0.1:26656/websocket".parse().unwrap(), + rpc_timeout: crate::config::default::rpc_timeout(), account_prefix: "".to_string(), key_name: "".to_string(), store_prefix: "".to_string(), @@ -350,7 +351,6 @@ pub mod test_utils { clock_drift: Duration::from_secs(5), trusting_period: Duration::from_secs(14 * 24 * 60 * 60), // 14 days trust_threshold: Default::default(), - peers: None, } } } diff --git a/relayer/src/config.rs b/relayer/src/config.rs index 66f0d8fb2c..d02b106659 100644 --- a/relayer/src/config.rs +++ b/relayer/src/config.rs @@ -1,16 +1,9 @@ //! Relayer configuration -use std::{ - fs, - fs::File, - io::Write, - path::{Path, PathBuf}, - time::Duration, -}; +use std::{fs, fs::File, io::Write, path::Path, time::Duration}; use serde_derive::{Deserialize, Serialize}; -use tendermint::Hash; -use tendermint_light_client::types::{Height, PeerId, TrustThreshold}; +use tendermint_light_client::types::TrustThreshold; use ibc::ics24_host::identifier::{ChainId, PortId}; @@ -20,7 +13,7 @@ use crate::error; pub mod default { use super::*; - pub fn timeout() -> Duration { + pub fn rpc_timeout() -> Duration { Duration::from_secs(10) } @@ -77,9 +70,6 @@ impl Default for Strategy { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GlobalConfig { - #[serde(default = "default::timeout", with = "humantime_serde")] - pub timeout: Duration, - #[serde(default)] pub strategy: Strategy, @@ -91,7 +81,6 @@ pub struct GlobalConfig { impl Default for GlobalConfig { fn default() -> Self { Self { - timeout: default::timeout(), strategy: Strategy::default(), log_level: "info".to_string(), } @@ -104,6 +93,8 @@ pub struct ChainConfig { pub rpc_addr: tendermint_rpc::Url, pub websocket_addr: tendermint_rpc::Url, pub grpc_addr: tendermint_rpc::Url, + #[serde(default = "default::rpc_timeout", with = "humantime_serde")] + pub rpc_timeout: Duration, pub account_prefix: String, pub key_name: String, pub store_prefix: String, @@ -118,34 +109,6 @@ pub struct ChainConfig { pub trusting_period: Duration, #[serde(default)] pub trust_threshold: TrustThreshold, - - // initially empty, to configure with the `light add/rm` commands - #[serde(skip_serializing_if = "Option::is_none")] - pub peers: Option, -} - -impl ChainConfig { - pub fn primary(&self) -> Option<&LightClientConfig> { - let peers = self.peers.as_ref()?; - peers.light_client(peers.primary) - } - - pub fn light_client(&self, id: PeerId) -> Option<&LightClientConfig> { - let peers = self.peers.as_ref()?; - peers.light_client(id) - } - - pub fn witnesses(&self) -> Option> { - let peers = self.peers.as_ref()?; - - Some( - peers - .light_clients - .iter() - .filter(|p| p.peer_id != peers.primary) - .collect(), - ) - } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -161,55 +124,6 @@ pub struct RelayPath { pub b_port: PortId, } -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct PeersConfig { - pub primary: PeerId, - pub light_clients: Vec, -} - -impl PeersConfig { - pub fn light_client(&self, id: PeerId) -> Option<&LightClientConfig> { - self.light_clients.iter().find(|p| p.peer_id == id) - } - - pub fn light_client_mut(&mut self, id: PeerId) -> Option<&mut LightClientConfig> { - self.light_clients.iter_mut().find(|p| p.peer_id == id) - } -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct LightClientConfig { - pub peer_id: PeerId, - pub address: tendermint_rpc::Url, - #[serde(default = "default::timeout", with = "humantime_serde")] - pub timeout: Duration, - pub trusted_header_hash: Hash, - pub trusted_height: Height, - pub store: StoreConfig, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(tag = "type")] -pub enum StoreConfig { - #[serde(rename = "disk")] - Disk { path: PathBuf }, - #[serde(rename = "memory")] - Memory { - #[serde(skip)] - dummy: (), - }, -} - -impl StoreConfig { - pub fn disk(path: PathBuf) -> Self { - Self::Disk { path } - } - - pub fn memory() -> Self { - Self::Memory { dummy: () } - } -} - /// Attempt to load and parse the TOML config file as a `Config`. pub fn parse(path: impl AsRef) -> Result { let config_toml = diff --git a/relayer/src/event/monitor.rs b/relayer/src/event/monitor.rs index 2f016b749c..1d2286e138 100644 --- a/relayer/src/event/monitor.rs +++ b/relayer/src/event/monitor.rs @@ -32,8 +32,16 @@ impl EventBatch { type SubscriptionResult = Result; type SubscriptionStream = dyn Stream + Send + Sync + Unpin; -/// Connect to a TM node, receive push events over a websocket and filter them for the +/// Connect to a Tendermint node, subscribe to a set of queries, +/// receive push events over a websocket, and filter them for the /// event handler. +/// +/// The default events that are queried are: +/// - [`EventType::NewBlock`] +/// - [`EventType::Tx`] +/// +/// Those can be extending or overriden using +/// [`EventMonitor::add_query`] and [`EventMonitor::set_queries`]. pub struct EventMonitor { chain_id: ChainId, /// WebSocket to collect events from @@ -87,6 +95,22 @@ impl EventMonitor { Ok((monitor, rx)) } + /// Set the queries to subscribe to. + /// + /// ## Note + /// For this change to take effect, one has to [`subscribe`] again. + pub fn set_queries(&mut self, queries: Vec) { + self.event_queries = queries; + } + + /// Add a new query to subscribe to. + /// + /// ## Note + /// For this change to take effect, one has to [`subscribe`] again. + pub fn add_query(&mut self, query: Query) { + self.event_queries.push(query); + } + /// Clear the current subscriptions, and subscribe again to all queries. pub fn subscribe(&mut self) -> Result<(), BoxError> { let mut subscriptions = vec![]; diff --git a/relayer/src/light_client/tendermint.rs b/relayer/src/light_client/tendermint.rs index 239032b776..f5648b4c6b 100644 --- a/relayer/src/light_client/tendermint.rs +++ b/relayer/src/light_client/tendermint.rs @@ -12,8 +12,11 @@ use tendermint_light_client::{ types::{LightBlock, PeerId, Status}, }; -use ibc::{downcast, ics02_client::client_state::AnyClientState}; -use ibc::{ics02_client::client_type::ClientType, ics24_host::identifier::ChainId}; +use ibc::{ + downcast, + ics02_client::{client_state::AnyClientState, client_type::ClientType}, + ics24_host::identifier::ChainId, +}; use crate::{ chain::CosmosSdkChain, @@ -56,19 +59,15 @@ impl super::LightClient for LightClient { } impl LightClient { - pub fn from_config(config: &ChainConfig) -> Result { + pub fn from_config(config: &ChainConfig, peer_id: PeerId) -> Result { let rpc_client = rpc::HttpClient::new(config.rpc_addr.clone()) .map_err(|e| error::Kind::LightClient(config.rpc_addr.to_string()).context(e))?; - let peer = config.primary().ok_or_else(|| { - error::Kind::LightClient(config.rpc_addr.to_string()).context("no primary peer") - })?; - - let io = components::io::ProdIo::new(peer.peer_id, rpc_client, Some(peer.timeout)); + let io = components::io::ProdIo::new(peer_id, rpc_client, Some(config.rpc_timeout)); Ok(Self { chain_id: config.id.clone(), - peer_id: peer.peer_id, + peer_id, io, }) } diff --git a/relayer/tests/config/fixtures/relayer_conf_example.toml b/relayer/tests/config/fixtures/relayer_conf_example.toml index 016a8455c3..234c133427 100644 --- a/relayer/tests/config/fixtures/relayer_conf_example.toml +++ b/relayer/tests/config/fixtures/relayer_conf_example.toml @@ -1,5 +1,4 @@ [global] -timeout = '10s' strategy = 'naive' log_level = 'error' @@ -8,6 +7,7 @@ id = 'chain_A' rpc_addr = 'http://127.0.0.1:26657' grpc_addr = 'http://127.0.0.1:9090' websocket_addr = 'ws://localhost:26657/websocket' +rpc_timeout = '10s' account_prefix = 'cosmos' key_name = 'testkey' store_prefix = 'ibc' @@ -28,6 +28,7 @@ id = 'chain_B' rpc_addr = 'http://127.0.0.1:26557' grpc_addr = 'http://127.0.0.1:9090' websocket_addr = 'ws://localhost:26557/websocket' +rpc_timeout = '10s' account_prefix = 'cosmos' key_name = 'testkey' store_prefix = 'ibc' diff --git a/relayer/tests/config/fixtures/simple_config.toml b/relayer/tests/config/fixtures/simple_config.toml index ce62e6d952..bd0bd6a8b2 100644 --- a/relayer/tests/config/fixtures/simple_config.toml +++ b/relayer/tests/config/fixtures/simple_config.toml @@ -3,7 +3,6 @@ title = "IBC Relayer Config Example" [global] -timeout = "10s" strategy = "naive" [[chains]] @@ -11,6 +10,7 @@ strategy = "naive" rpc_addr = "http://127.0.0.1:26657" grpc_addr = 'http://127.0.0.1:9090' websocket_addr = 'ws://localhost:26657/websocket' + rpc_timeout = '10s' account_prefix = "cosmos" key_name = "testkey" store_prefix = "ibc" @@ -21,14 +21,12 @@ strategy = "naive" gas_price = "0.025stake" trusting_period = "336h" - # TODO: Move to light client config - peer_id = "BADFADAD0BEFEEDC0C0ADEADBEEFC0FFEEFACADE" - [[chains]] id = "chain_B" rpc_addr = "http://127.0.0.1:26557" grpc_addr = 'http://127.0.0.1:9090' websocket_addr = 'ws://localhost:26557/websocket' + rpc_timeout = '10s' account_prefix = "cosmos" key_name = "testkey" store_prefix = "ibc" @@ -39,9 +37,6 @@ strategy = "naive" gas_price = "0.025stake" trusting_period = "336h" - # TODO: Move to light client config - peer_id = "DC0C0ADEADBEEFC0FFEEFACADEBADFADAD0BEFEE" - [[connections]] a_chain = "ibc1" b_chain = "ibc0" diff --git a/scripts/dev-env b/scripts/dev-env index 3dfd929573..eeb3dd87ba 100755 --- a/scripts/dev-env +++ b/scripts/dev-env @@ -35,7 +35,7 @@ CHAIN_1_ID="$3" CHAIN_2_ID="$4" SETUP_CHAINS="$(dirname "$0")/setup-chains" -INIT_CLIENTS="$(dirname "$0")/init-clients" +INIT_CLIENTS="$(dirname "$0")/init-hermes" "$SETUP_CHAINS" "$CHAIN_0_ID" "$CHAIN_1_ID" "$CHAIN_2_ID" "$INIT_CLIENTS" "$CONFIG_FILE" "$CHAIN_0_ID" "$CHAIN_1_ID" "$CHAIN_2_ID" diff --git a/scripts/init-clients b/scripts/init-clients deleted file mode 100755 index 5843247686..0000000000 --- a/scripts/init-clients +++ /dev/null @@ -1,107 +0,0 @@ -#!/bin/bash -e - -# Initialize the light clients in the relayer configuration. - -usage() { - echo "Usage: $0 CONFIG_FILE CHAIN_0_ID CHAIN_1_ID [CHAIN_2_ID]" - echo "Example: $0 ./config.toml ibc-0 ibc-1 ibc-2" - exit 1 -} - -missing() { - echo "Missing $1 parameter. Please check if all parameters were specified." - usage -} - -if [ -z "$1" ]; then - missing "CONFIG_FILE" -fi - -if [ -z "$2" ]; then - missing "CHAIN_0_ID" -fi - -if [ -z "$3" ]; then - missing "CHAIN_1_ID" -fi - - -if [ "$#" -gt 4 ]; then - echo "Incorrect number of parameters." - usage -fi - -CONFIG_FILE="$1" -CHAIN_0_ID="$2" -CHAIN_1_ID="$3" -CHAIN_2_ID="$4" - -if ! [ -f "$CONFIG_FILE" ]; then - echo "[CONFIG_FILE] ($1) does not exist or is not a file." - usage -fi - -if ! grep -q -s "$CHAIN_0_ID" "$CONFIG_FILE"; then - echo "error: configuration for chain [$CHAIN_0_ID] does not exist in file $CONFIG_FILE." - usage -fi - -if ! grep -q -s "$CHAIN_1_ID" "$CONFIG_FILE"; then - echo "error: configuration for chain [$CHAIN_1_ID] does not exist in file $CONFIG_FILE." - usage -fi - -if [ -n "$CHAIN_2_ID" ] && ! grep -q -s "$CHAIN_2_ID" "$CONFIG_FILE"; then - echo "error: configuration for chain [$CHAIN_2_ID] does not exist in file $CONFIG_FILE." - usage -fi - -GAIA_DATA="$(pwd)/data" - -CHAIN_0_RPC_PORT=26657 -CHAIN_0_RPC_ADDR="http://127.0.0.1:$CHAIN_0_RPC_PORT" -CHAIN_1_RPC_PORT=26557 -CHAIN_1_RPC_ADDR="http://127.0.0.1:$CHAIN_1_RPC_PORT" -CHAIN_2_RPC_PORT=26457 -CHAIN_2_RPC_ADDR="http://127.0.0.1:$CHAIN_2_RPC_PORT" - -echo "Building the Rust relayer..." -cargo build -q - -# cleanup the client entries from config -echo "Removing light client peers from configuration..." -cargo run -q --bin hermes -- -c "$CONFIG_FILE" light rm -c "$CHAIN_0_ID" --all -y || true -cargo run -q --bin hermes -- -c "$CONFIG_FILE" light rm -c "$CHAIN_1_ID" --all -y || true - -if [ -n "$CHAIN_2_ID" ]; then - cargo run -q --bin hermes -- -c "$CONFIG_FILE" light rm -c "$CHAIN_2_ID" --all -y || true -fi - -# set the primary peers for clients on each chain -echo "Adding primary peers to light client configuration..." -cargo run -q --bin hermes -- -c "$CONFIG_FILE" light add $CHAIN_0_RPC_ADDR -c "$CHAIN_0_ID" -f -p -s "$GAIA_DATA/$CHAIN_0_ID/data" -y -cargo run -q --bin hermes -- -c "$CONFIG_FILE" light add $CHAIN_1_RPC_ADDR -c "$CHAIN_1_ID" -f -p -s "$GAIA_DATA/$CHAIN_1_ID/data" -y - -if [ -n "$CHAIN_2_ID" ]; then - cargo run -q --bin hermes -- -c "$CONFIG_FILE" light add $CHAIN_2_RPC_ADDR -c "$CHAIN_2_ID" -f -p -s "$GAIA_DATA/$CHAIN_2_ID/data" -y -fi - -# set the secondary peers for clients on each chain -echo "Adding secondary peers to light client configuration..." -cargo run -q --bin hermes -- -c "$CONFIG_FILE" light add $CHAIN_0_RPC_ADDR -c "$CHAIN_0_ID" -s "$GAIA_DATA/$CHAIN_0_ID/data" -y --peer-id 2427F8D914A6862279B3326FA64F76E3BC06DB2E -cargo run -q --bin hermes -- -c "$CONFIG_FILE" light add $CHAIN_1_RPC_ADDR -c "$CHAIN_1_ID" -s "$GAIA_DATA/$CHAIN_1_ID/data" -y --peer-id A885BB3D3DFF6101188B462466AE926E7A6CD51E - -if [ -n "$CHAIN_2_ID" ]; then - cargo run -q --bin hermes -- -c "$CONFIG_FILE" light add $CHAIN_2_RPC_ADDR -c "$CHAIN_2_ID" -s "$GAIA_DATA/$CHAIN_2_ID/data" -y --peer-id 08B4625466AE926E7A5BB3D3DFF611E6CD118A88 -fi - -# add the key seeds to the keyring of each chain -echo "Importing keys..." -cargo run -q --bin hermes -- -c "$CONFIG_FILE" keys add "$CHAIN_0_ID" "$GAIA_DATA/$CHAIN_0_ID/key_seed.json" -cargo run -q --bin hermes -- -c "$CONFIG_FILE" keys add "$CHAIN_1_ID" "$GAIA_DATA/$CHAIN_1_ID/key_seed.json" - -if [ -n "$CHAIN_2_ID" ]; then - cargo run -q --bin hermes -- -c "$CONFIG_FILE" keys add "$CHAIN_2_ID" "$GAIA_DATA/$CHAIN_2_ID/key_seed.json" -fi - -echo "Done!" diff --git a/scripts/init-hermes b/scripts/init-hermes new file mode 100755 index 0000000000..f52ffe8708 --- /dev/null +++ b/scripts/init-hermes @@ -0,0 +1,73 @@ +#!/bin/bash -e + +# Initialize the light clients in the relayer configuration. + +usage() { + echo "Usage: $0 CONFIG_FILE CHAIN_0_ID CHAIN_1_ID [CHAIN_2_ID]" + echo "Example: $0 ./config.toml ibc-0 ibc-1 ibc-2" + exit 1 +} + +missing() { + echo "Missing $1 parameter. Please check if all parameters were specified." + usage +} + +if [ -z "$1" ]; then + missing "CONFIG_FILE" +fi + +if [ -z "$2" ]; then + missing "CHAIN_0_ID" +fi + +if [ -z "$3" ]; then + missing "CHAIN_1_ID" +fi + + +if [ "$#" -gt 4 ]; then + echo "Incorrect number of parameters." + usage +fi + +CONFIG_FILE="$1" +CHAIN_0_ID="$2" +CHAIN_1_ID="$3" +CHAIN_2_ID="$4" + +if ! [ -f "$CONFIG_FILE" ]; then + echo "[CONFIG_FILE] ($1) does not exist or is not a file." + usage +fi + +if ! grep -q -s "$CHAIN_0_ID" "$CONFIG_FILE"; then + echo "error: configuration for chain [$CHAIN_0_ID] does not exist in file $CONFIG_FILE." + usage +fi + +if ! grep -q -s "$CHAIN_1_ID" "$CONFIG_FILE"; then + echo "error: configuration for chain [$CHAIN_1_ID] does not exist in file $CONFIG_FILE." + usage +fi + +if [ -n "$CHAIN_2_ID" ] && ! grep -q -s "$CHAIN_2_ID" "$CONFIG_FILE"; then + echo "error: configuration for chain [$CHAIN_2_ID] does not exist in file $CONFIG_FILE." + usage +fi + +GAIA_DATA="$(pwd)/data" + +echo "Building the Rust relayer..." +cargo build -q --locked + +# add the key seeds to the keyring of each chain +echo "Importing keys..." +cargo run -q --bin hermes -- -c "$CONFIG_FILE" keys add "$CHAIN_0_ID" "$GAIA_DATA/$CHAIN_0_ID/key_seed.json" +cargo run -q --bin hermes -- -c "$CONFIG_FILE" keys add "$CHAIN_1_ID" "$GAIA_DATA/$CHAIN_1_ID/key_seed.json" + +if [ -n "$CHAIN_2_ID" ]; then + cargo run -q --bin hermes -- -c "$CONFIG_FILE" keys add "$CHAIN_2_ID" "$GAIA_DATA/$CHAIN_2_ID/key_seed.json" +fi + +echo "Done!"