From c7196a014e66dfb7156e452be1146f0b329965c1 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Thu, 24 Jun 2021 16:55:51 +0200 Subject: [PATCH 1/8] Config validation --- relayer-cli/src/commands.rs | 17 +++--- relayer-cli/src/commands/config/validate.rs | 61 +++++++++++++++++++-- relayer-cli/src/components.rs | 32 +++++++++-- 3 files changed, 91 insertions(+), 19 deletions(-) diff --git a/relayer-cli/src/commands.rs b/relayer-cli/src/commands.rs index dd7118896a..db2d62f4ca 100644 --- a/relayer-cli/src/commands.rs +++ b/relayer-cli/src/commands.rs @@ -16,10 +16,10 @@ use crate::config::Config; use crate::DEFAULT_CONFIG_PATH; use self::{ - create::CreateCmds, keys::KeysCmd, listen::ListenCmd, query::QueryCmd, start::StartCmd, - tx::TxCmd, update::UpdateCmds, upgrade::UpgradeCmds, version::VersionCmd, + config::ConfigCmd, create::CreateCmds, keys::KeysCmd, listen::ListenCmd, + misbehaviour::MisbehaviourCmd, query::QueryCmd, start::StartCmd, tx::TxCmd, update::UpdateCmds, + upgrade::UpgradeCmds, version::VersionCmd, }; -use crate::commands::misbehaviour::MisbehaviourCmd; mod config; mod create; @@ -38,11 +38,6 @@ pub fn default_config_file() -> Option { dirs_next::home_dir().map(|home| home.join(DEFAULT_CONFIG_PATH)) } -// TODO: Re-add the `config` subcommand -// /// The `config` subcommand -// #[options(help = "manipulate the relayer configuration")] -// Config(ConfigCmd), - /// Cli Subcommands #[derive(Command, Debug, Options, Runnable)] pub enum CliCmd { @@ -50,6 +45,10 @@ pub enum CliCmd { #[options(help = "Get usage information")] Help(Help), + /// The `config` subcommand + #[options(help = "Validate Hermes configuration file")] + Config(ConfigCmd), + /// The `keys` subcommand #[options(help = "Manage keys in the relayer for each chain")] Keys(KeysCmd), @@ -68,7 +67,7 @@ pub enum CliCmd { /// The `start` subcommand #[options(help = "Start the relayer in multi-chain mode. \ - Relays packets and channel handshake messages between all chains in the config.")] + Relays packets and open handshake messages between all chains in the config.")] Start(StartCmd), /// The `query` subcommand diff --git a/relayer-cli/src/commands/config/validate.rs b/relayer-cli/src/commands/config/validate.rs index c642beeaae..2f257088f1 100644 --- a/relayer-cli/src/commands/config/validate.rs +++ b/relayer-cli/src/commands/config/validate.rs @@ -1,19 +1,72 @@ +use std::collections::BTreeSet; + +use abscissa_core::config::Reader; use abscissa_core::{Command, Options, Runnable}; +use thiserror::Error; + +use ibc::ics24_host::identifier::ChainId; +use crate::application::CliApp; use crate::conclude::Output; use crate::prelude::*; #[derive(Command, Debug, Options)] pub struct ValidateCmd {} +/// Captures all the possible outcomes of performing syntactic +/// validation of a Hermes configuration file. +#[derive(Error, Debug)] +pub enum Diagnostic { + /// Validation passed successfully + #[error("config validation passed")] + Ok, + + /// No chain was configured + #[error("config file does not specify any chain")] + ZeroChains, + + /// Only one chain was configured + #[error("config file specifies a single chain, Hermes has limited functionality in this mode (e.g., some queries)")] + OneChain, + + /// Duplicate chains configured + #[error("config file has duplicate entry for the chain with id {0}")] + DuplicateChains(ChainId), +} + +/// Validation method for syntactic validation of the input +/// configuration file. +fn validate_config(config: Reader) -> Diagnostic { + // Check for empty or solitary chains config. + if config.chains.is_empty() { + // No chain is preconfigured + return Diagnostic::ZeroChains; + } else if config.chains.len() == 1 { + // Only one chain is preconfigured, not exactly an error but still useful to mention + return Diagnostic::OneChain; + } + + // Check for duplicate chain configuration. + let mut unique_chain_ids = BTreeSet::new(); + for chain_id in config.chains.iter().map(|c| c.id.clone()) { + if !unique_chain_ids.insert(chain_id.clone()) { + return Diagnostic::DuplicateChains(chain_id); + } + } + + Diagnostic::Ok +} + impl Runnable for ValidateCmd { /// Validate the loaded configuration. fn run(&self) { let config = app_config(); - info!("Loaded configuration: {:?}", *config); - - // TODO: Validate configuration + debug!("loaded configuration: {:#?}", *config); - Output::with_success().exit(); + let diagnostic = validate_config(config); + match diagnostic { + Diagnostic::Ok => Output::success(format!("{}", diagnostic)).exit(), + _ => Output::error(format!("{}", diagnostic)).exit(), + } } } diff --git a/relayer-cli/src/components.rs b/relayer-cli/src/components.rs index 025708caa6..36fba98cde 100644 --- a/relayer-cli/src/components.rs +++ b/relayer-cli/src/components.rs @@ -1,6 +1,6 @@ use std::io; -use abscissa_core::{Component, FrameworkError}; +use abscissa_core::{Component, FrameworkError, FrameworkErrorKind}; use tracing_subscriber::{ fmt::{ format::{DefaultFields, Format, Full, Json, JsonFields}, @@ -34,7 +34,7 @@ impl JsonTracing { /// Creates a new [`Tracing`] component #[allow(trivial_casts)] pub fn new(cfg: GlobalConfig) -> Result { - let filter = build_tracing_filter(cfg.log_level); + let filter = build_tracing_filter(cfg.log_level)?; // Note: JSON formatter is un-affected by ANSI 'color' option. Set to 'false'. let use_color = false; @@ -65,7 +65,7 @@ impl PrettyTracing { /// Creates a new [`Tracing`] component #[allow(trivial_casts)] pub fn new(cfg: GlobalConfig) -> Result { - let filter = build_tracing_filter(cfg.log_level); + let filter = build_tracing_filter(cfg.log_level)?; // Construct a tracing subscriber with the supplied filter and enable reloading. let builder = FmtSubscriber::builder() @@ -92,12 +92,32 @@ fn enable_ansi() -> bool { atty::is(atty::Stream::Stdout) && atty::is(atty::Stream::Stderr) } -fn build_tracing_filter(log_level: String) -> String { +/// Builds a tracing filter based on the input `log_level`. +/// Enables tracing exclusively for the relayer crates. +/// Returns error if the given `log_level` is invalid. +fn build_tracing_filter(log_level: String) -> Result { let target_crates = ["ibc_relayer", "ibc_relayer_cli"]; - target_crates + // SAFETY: unwrap() below works as long as `target_crates` is not empty. + let directive_raw = target_crates .iter() .map(|&c| format!("{}={}", c, log_level)) .reduce(|a, b| format!("{},{}", a, b)) - .unwrap() + .unwrap(); + + // Build the filter directive + match EnvFilter::try_new(directive_raw.clone()) { + Ok(out) => Ok(out), + Err(e) => { + eprintln!( + "Unable to initialize Hermes from filter directive {:?}: {}", + directive_raw, e + ); + eprintln!( + "Help: The filter level ({:?}) in your configuration file is probably incorrect", + log_level + ); + Err(FrameworkErrorKind::ParseError.context(e).into()) + } + } } From 1270adb32373d05783e42701d6f49218504f6ed5 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Thu, 24 Jun 2021 18:01:32 +0200 Subject: [PATCH 2/8] Reorganized a bit; using Result --- relayer-cli/src/application.rs | 8 ++- relayer-cli/src/commands/config/validate.rs | 61 ++------------------- relayer-cli/src/components.rs | 5 +- relayer-cli/src/config.rs | 50 +++++++++++++---- 4 files changed, 53 insertions(+), 71 deletions(-) diff --git a/relayer-cli/src/application.rs b/relayer-cli/src/application.rs index 2ee8bf7ccc..f24004eeb4 100644 --- a/relayer-cli/src/application.rs +++ b/relayer-cli/src/application.rs @@ -4,12 +4,12 @@ use abscissa_core::terminal::component::Terminal; use abscissa_core::{ application::{self, AppCell}, component::Component, - config, Application, Configurable, FrameworkError, StandardPaths, + config, Application, Configurable, FrameworkError, FrameworkErrorKind, StandardPaths, }; use crate::components::{JsonTracing, PrettyTracing}; use crate::entry::EntryPoint; -use crate::{commands::CliCmd, config::Config}; +use crate::{commands::CliCmd, config::validate_config, config::Config}; /// Application state pub static APPLICATION: AppCell = AppCell::new(); @@ -110,7 +110,11 @@ impl Application for CliApp { fn after_config(&mut self, config: Self::Cfg) -> Result<(), FrameworkError> { // Configure components self.state.components.after_config(&config)?; + + validate_config(&config) + .map_err(|validation_err| FrameworkErrorKind::ConfigError.context(validation_err))?; self.config = Some(config); + Ok(()) } diff --git a/relayer-cli/src/commands/config/validate.rs b/relayer-cli/src/commands/config/validate.rs index 2f257088f1..79c2fd1939 100644 --- a/relayer-cli/src/commands/config/validate.rs +++ b/relayer-cli/src/commands/config/validate.rs @@ -1,72 +1,21 @@ -use std::collections::BTreeSet; - -use abscissa_core::config::Reader; use abscissa_core::{Command, Options, Runnable}; -use thiserror::Error; - -use ibc::ics24_host::identifier::ChainId; -use crate::application::CliApp; use crate::conclude::Output; +use crate::config; use crate::prelude::*; #[derive(Command, Debug, Options)] pub struct ValidateCmd {} -/// Captures all the possible outcomes of performing syntactic -/// validation of a Hermes configuration file. -#[derive(Error, Debug)] -pub enum Diagnostic { - /// Validation passed successfully - #[error("config validation passed")] - Ok, - - /// No chain was configured - #[error("config file does not specify any chain")] - ZeroChains, - - /// Only one chain was configured - #[error("config file specifies a single chain, Hermes has limited functionality in this mode (e.g., some queries)")] - OneChain, - - /// Duplicate chains configured - #[error("config file has duplicate entry for the chain with id {0}")] - DuplicateChains(ChainId), -} - -/// Validation method for syntactic validation of the input -/// configuration file. -fn validate_config(config: Reader) -> Diagnostic { - // Check for empty or solitary chains config. - if config.chains.is_empty() { - // No chain is preconfigured - return Diagnostic::ZeroChains; - } else if config.chains.len() == 1 { - // Only one chain is preconfigured, not exactly an error but still useful to mention - return Diagnostic::OneChain; - } - - // Check for duplicate chain configuration. - let mut unique_chain_ids = BTreeSet::new(); - for chain_id in config.chains.iter().map(|c| c.id.clone()) { - if !unique_chain_ids.insert(chain_id.clone()) { - return Diagnostic::DuplicateChains(chain_id); - } - } - - Diagnostic::Ok -} - impl Runnable for ValidateCmd { /// Validate the loaded configuration. fn run(&self) { let config = app_config(); - debug!("loaded configuration: {:#?}", *config); + trace!("loaded configuration: {:#?}", *config); - let diagnostic = validate_config(config); - match diagnostic { - Diagnostic::Ok => Output::success(format!("{}", diagnostic)).exit(), - _ => Output::error(format!("{}", diagnostic)).exit(), + match config::validate_config(&config) { + Ok(_) => Output::success("validation passed successfully").exit(), + Err(e) => Output::error(format!("{}", e)).exit(), } } } diff --git a/relayer-cli/src/components.rs b/relayer-cli/src/components.rs index 36fba98cde..ce2595c276 100644 --- a/relayer-cli/src/components.rs +++ b/relayer-cli/src/components.rs @@ -14,6 +14,8 @@ use tracing_subscriber::{ use ibc_relayer::config::GlobalConfig; +use crate::config; + /// Custom types to simplify the `Tracing` definition below type JsonFormatter = TracingFormatter, StdWriter>; type PrettyFormatter = TracingFormatter, StdWriter>; @@ -109,6 +111,7 @@ fn build_tracing_filter(log_level: String) -> Result match EnvFilter::try_new(directive_raw.clone()) { Ok(out) => Ok(out), Err(e) => { + let our_err = config::Error::InvalidLogLevel(log_level.clone(), e.to_string()); eprintln!( "Unable to initialize Hermes from filter directive {:?}: {}", directive_raw, e @@ -117,7 +120,7 @@ fn build_tracing_filter(log_level: String) -> Result "Help: The filter level ({:?}) in your configuration file is probably incorrect", log_level ); - Err(FrameworkErrorKind::ParseError.context(e).into()) + Err(FrameworkErrorKind::ConfigError.context(our_err).into()) } } } diff --git a/relayer-cli/src/config.rs b/relayer-cli/src/config.rs index cf434c7d10..7f780bb049 100644 --- a/relayer-cli/src/config.rs +++ b/relayer-cli/src/config.rs @@ -4,20 +4,46 @@ //! application's configuration file and/or command-line options //! for specifying it. -use std::path::PathBuf; +use std::collections::BTreeSet; -use abscissa_core::{error::BoxError, EntryPoint, Options}; - -use crate::commands::CliCmd; +use thiserror::Error; +use ibc::ics24_host::identifier::ChainId; pub use ibc_relayer::config::Config; -/// Get the path to configuration file -pub fn config_path() -> Result { - let mut args = std::env::args(); - assert!(args.next().is_some(), "expected one argument but got zero"); - let args = args.collect::>(); - let app = EntryPoint::::parse_args_default(args.as_slice())?; - let config_path = app.config.ok_or("no config file specified")?; - Ok(config_path) +/// Specifies all the possible syntactic errors +/// that a Hermes configuration file could contain. +#[derive(Error, Debug)] +pub enum Error { + /// No chain was configured + #[error("config file does not specify any chain")] + ZeroChains, + + /// The log level is invalid + #[error("config file specifies an invalid log level ('{0}'), caused by: {1}")] + InvalidLogLevel(String, String), + + /// Duplicate chains configured + #[error("config file has duplicate entry for the chain with id {0}")] + DuplicateChains(ChainId), +} + +/// Validation method for syntactic validation of the input +/// configuration file. +pub fn validate_config(config: &Config) -> Result<(), Error> { + // Check for empty or solitary chains config. + if config.chains.is_empty() { + // No chain is preconfigured + return Err(Error::ZeroChains); + } + + // Check for duplicate chain configuration. + let mut unique_chain_ids = BTreeSet::new(); + for chain_id in config.chains.iter().map(|c| c.id.clone()) { + if !unique_chain_ids.insert(chain_id.clone()) { + return Err(Error::DuplicateChains(chain_id)); + } + } + + Ok(()) } From 38cc315d2c1a125d4d10580f0c9b1fbd806ae179 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Fri, 25 Jun 2021 12:01:27 +0200 Subject: [PATCH 3/8] Serialization via serde + enum for log levels --- relayer-cli/src/components.rs | 10 +++------ relayer/src/config.rs | 41 ++++++++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/relayer-cli/src/components.rs b/relayer-cli/src/components.rs index ce2595c276..5c6872ca50 100644 --- a/relayer-cli/src/components.rs +++ b/relayer-cli/src/components.rs @@ -36,7 +36,7 @@ impl JsonTracing { /// Creates a new [`Tracing`] component #[allow(trivial_casts)] pub fn new(cfg: GlobalConfig) -> Result { - let filter = build_tracing_filter(cfg.log_level)?; + let filter = build_tracing_filter(cfg.log_level.to_string())?; // Note: JSON formatter is un-affected by ANSI 'color' option. Set to 'false'. let use_color = false; @@ -67,7 +67,7 @@ impl PrettyTracing { /// Creates a new [`Tracing`] component #[allow(trivial_casts)] pub fn new(cfg: GlobalConfig) -> Result { - let filter = build_tracing_filter(cfg.log_level)?; + let filter = build_tracing_filter(cfg.log_level.to_string())?; // Construct a tracing subscriber with the supplied filter and enable reloading. let builder = FmtSubscriber::builder() @@ -111,15 +111,11 @@ fn build_tracing_filter(log_level: String) -> Result match EnvFilter::try_new(directive_raw.clone()) { Ok(out) => Ok(out), Err(e) => { - let our_err = config::Error::InvalidLogLevel(log_level.clone(), e.to_string()); + let our_err = config::Error::InvalidLogLevel(log_level, e.to_string()); eprintln!( "Unable to initialize Hermes from filter directive {:?}: {}", directive_raw, e ); - eprintln!( - "Help: The filter level ({:?}) in your configuration file is probably incorrect", - log_level - ); Err(FrameworkErrorKind::ConfigError.context(our_err).into()) } } diff --git a/relayer/src/config.rs b/relayer/src/config.rs index 87cf6e0c64..0eb40d4b3c 100644 --- a/relayer/src/config.rs +++ b/relayer/src/config.rs @@ -51,6 +51,7 @@ pub mod default { #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct Config { + #[serde(default)] pub global: GlobalConfig, #[serde(default)] pub telemetry: TelemetryConfig, @@ -83,21 +84,49 @@ impl Default for Strategy { } } +/// Log levels are wrappers over [`tracing_core::Level`]. +/// +/// [`tracing_core::Level`]: https://docs.rs/tracing-core/0.1.17/tracing_core/struct.Level.html +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum LogLevel { + Trace, + Debug, + Info, + Warn, + Error, +} + +impl Default for LogLevel { + fn default() -> Self { + Self::Info + } +} + +impl fmt::Display for LogLevel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LogLevel::Trace => write!(f, "trace"), + LogLevel::Debug => write!(f, "debug"), + LogLevel::Info => write!(f, "info"), + LogLevel::Warn => write!(f, "warn"), + LogLevel::Error => write!(f, "error"), + } + } +} + #[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(default)] pub struct GlobalConfig { - #[serde(default)] 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 log_level: LogLevel, } impl Default for GlobalConfig { fn default() -> Self { Self { strategy: Strategy::default(), - log_level: "info".to_string(), + log_level: LogLevel::default(), } } } From 9f7c7b2239875d0af62f3c63741e9a846e1a36f8 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Fri, 25 Jun 2021 13:53:54 +0200 Subject: [PATCH 4/8] Avoid throwing ZeroChains error except for start command --- relayer-cli/src/commands/start.rs | 6 ++++++ relayer-cli/src/config.rs | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/relayer-cli/src/commands/start.rs b/relayer-cli/src/commands/start.rs index 41cce404e7..8086398904 100644 --- a/relayer-cli/src/commands/start.rs +++ b/relayer-cli/src/commands/start.rs @@ -6,6 +6,7 @@ use ibc_relayer::config::Config; use ibc_relayer::supervisor::Supervisor; use crate::conclude::Output; +use crate::config; use crate::prelude::*; #[derive(Clone, Command, Debug, Options)] @@ -15,6 +16,11 @@ impl Runnable for StartCmd { fn run(&self) { let config = app_config(); + // No chain is preconfigured + if config.chains.is_empty() { + Output::error(config::Error::ZeroChains).exit(); + }; + match spawn_supervisor(config.clone()).and_then(|s| { info!("Hermes has started"); s.run() diff --git a/relayer-cli/src/config.rs b/relayer-cli/src/config.rs index 7f780bb049..b7da1601f9 100644 --- a/relayer-cli/src/config.rs +++ b/relayer-cli/src/config.rs @@ -31,12 +31,6 @@ pub enum Error { /// Validation method for syntactic validation of the input /// configuration file. pub fn validate_config(config: &Config) -> Result<(), Error> { - // Check for empty or solitary chains config. - if config.chains.is_empty() { - // No chain is preconfigured - return Err(Error::ZeroChains); - } - // Check for duplicate chain configuration. let mut unique_chain_ids = BTreeSet::new(); for chain_id in config.chains.iter().map(|c| c.id.clone()) { From 8198f25667758bbb3f74a351b065d550da59e2f5 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Fri, 25 Jun 2021 15:01:40 +0200 Subject: [PATCH 5/8] Apply suggestions from code review Co-authored-by: Anca Zamfir --- relayer-cli/src/config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/relayer-cli/src/config.rs b/relayer-cli/src/config.rs index b7da1601f9..71cc77a35a 100644 --- a/relayer-cli/src/config.rs +++ b/relayer-cli/src/config.rs @@ -15,7 +15,7 @@ pub use ibc_relayer::config::Config; /// that a Hermes configuration file could contain. #[derive(Error, Debug)] pub enum Error { - /// No chain was configured + /// No chain is configured #[error("config file does not specify any chain")] ZeroChains, @@ -28,7 +28,7 @@ pub enum Error { DuplicateChains(ChainId), } -/// Validation method for syntactic validation of the input +/// Method for syntactic validation of the input /// configuration file. pub fn validate_config(config: &Config) -> Result<(), Error> { // Check for duplicate chain configuration. From eb10e9900f00b95320f97e020c20be9859851f37 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Fri, 25 Jun 2021 15:25:57 +0200 Subject: [PATCH 6/8] Added deny_unknown_fields serde attribute. --- relayer/src/config.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/relayer/src/config.rs b/relayer/src/config.rs index 0eb40d4b3c..bf10891898 100644 --- a/relayer/src/config.rs +++ b/relayer/src/config.rs @@ -50,6 +50,7 @@ pub mod default { } #[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] pub struct Config { #[serde(default)] pub global: GlobalConfig, @@ -116,7 +117,7 @@ impl fmt::Display for LogLevel { } #[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(default)] +#[serde(default, deny_unknown_fields)] pub struct GlobalConfig { pub strategy: Strategy, pub log_level: LogLevel, @@ -132,6 +133,7 @@ impl Default for GlobalConfig { } #[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] pub struct TelemetryConfig { pub enabled: bool, pub host: String, @@ -149,6 +151,7 @@ impl Default for TelemetryConfig { } #[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] pub struct ChainConfig { pub id: ChainId, pub rpc_addr: tendermint_rpc::Url, From 0db847bcf57bf7b3ab0ffbc00335102c90fbb211 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Fri, 25 Jun 2021 15:29:06 +0200 Subject: [PATCH 7/8] changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d980f575cc..dc46574a54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,16 @@ ## Unreleased +### FEATURES + +- [ibc-relayer-cli] + - Added `config validate` CLI to Hermes ([#600]) + ### IMPROVEMENTS - Update to `tendermint-rs` v0.20.0 ([#1125]) +[#600]: https://github.com/informalsystems/ibc-rs/issues/600 [#1125]: https://github.com/informalsystems/ibc-rs/issues/1125 From ea9582ad8f89ae20b6aa1019c2eca6eb03851edb Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Fri, 25 Jun 2021 15:32:29 +0200 Subject: [PATCH 8/8] Fix comment --- relayer-cli/src/components.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relayer-cli/src/components.rs b/relayer-cli/src/components.rs index 5c6872ca50..fbb9f81901 100644 --- a/relayer-cli/src/components.rs +++ b/relayer-cli/src/components.rs @@ -96,7 +96,7 @@ fn enable_ansi() -> bool { /// Builds a tracing filter based on the input `log_level`. /// Enables tracing exclusively for the relayer crates. -/// Returns error if the given `log_level` is invalid. +/// Returns error if the filter failed to build. fn build_tracing_filter(log_level: String) -> Result { let target_crates = ["ibc_relayer", "ibc_relayer_cli"];