1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
//! Definition of all the Hermes subcommands
mod clear;
mod completions;
mod config;
mod create;
mod evidence;
mod fee;
mod health;
mod keys;
mod listen;
mod logs;
mod misbehaviour;
mod query;
mod start;
mod tx;
mod update;
mod upgrade;
mod version;
use self::{
clear::ClearCmds, completions::CompletionsCmd, config::ConfigCmd, create::CreateCmds,
evidence::EvidenceCmd, fee::FeeCmd, health::HealthCheckCmd, keys::KeysCmd, listen::ListenCmd,
logs::LogsCmd, misbehaviour::MisbehaviourCmd, query::QueryCmd, start::StartCmd, tx::TxCmd,
update::UpdateCmds, upgrade::UpgradeCmds, version::VersionCmd,
};
use core::time::Duration;
use std::path::PathBuf;
use abscissa_core::clap::Parser;
use abscissa_core::{config::Override, Command, Configurable, FrameworkError, Runnable};
use tracing::{error, info};
use crate::DEFAULT_CONFIG_PATH;
use ibc_relayer::config::{ChainConfig, Config};
/// Default configuration file path
pub fn default_config_file() -> Option<PathBuf> {
dirs_next::home_dir().map(|home| home.join(DEFAULT_CONFIG_PATH))
}
/// Cli Subcommands
#[derive(Command, Parser, Debug, Runnable)]
pub enum CliCmd {
/// Generate a new Hermes configuration file or validate an existing one
#[clap(subcommand)]
Config(ConfigCmd),
/// Manage keys in the relayer for each chain
#[clap(subcommand)]
Keys(KeysCmd),
/// Create objects (client, connection, or channel) on chains
#[clap(subcommand)]
Create(CreateCmds),
/// Update objects (clients) on chains
#[clap(subcommand)]
Update(UpdateCmds),
/// Upgrade objects (clients) after chain upgrade
#[clap(subcommand)]
Upgrade(UpgradeCmds),
/// Clear objects, such as outstanding packets on a channel.
#[clap(subcommand)]
Clear(ClearCmds),
/// Start the relayer in multi-chain mode.
///
/// Relays packets and open handshake messages between all chains in the config.
Start(StartCmd),
/// Query objects from the chain
#[clap(subcommand)]
Query(QueryCmd),
/// Create and send IBC transactions
#[clap(subcommand)]
Tx(TxCmd),
/// Interact with the fee middleware
#[clap(subcommand)]
Fee(FeeCmd),
/// Listen to and display IBC events emitted by a chain
Listen(ListenCmd),
/// Listen to client update IBC events and handle misbehaviour
Misbehaviour(MisbehaviourCmd),
/// Update tracing log directives
#[clap(subcommand)]
Logs(LogsCmd),
/// Listen to block events and handles evidence
Evidence(EvidenceCmd),
/// The `version` subcommand, retained for backward compatibility.
Version(VersionCmd),
/// Performs a health check of all chains in the config
HealthCheck(HealthCheckCmd),
/// Generate auto-complete scripts for different shells.
#[clap(display_order = 1000)]
Completions(CompletionsCmd),
}
/// This trait allows you to define how application configuration is loaded.
impl Configurable<Config> for CliCmd {
/// Location of the configuration file
/// This is called only when the `-c` command-line option is omitted.
fn config_path(&self) -> Option<PathBuf> {
let path = default_config_file();
match path {
Some(path) if path.exists() => {
info!("using default configuration from '{}'", path.display());
Some(path)
}
Some(path) => {
// No file exists at the config path
error!("could not find configuration file at '{}'", path.display());
error!("for an example, please see https://hermes.informal.systems/config.html#example-configuration-file");
None
}
None => {
// The path to the default config file could not be found
error!("could not find default configuration file");
error!(
"please create one at '~/{}' or specify it with the '-c'/'--config' flag",
DEFAULT_CONFIG_PATH
);
error!("for an example, please see https://hermes.informal.systems/config.html#example-configuration-file");
None
}
}
}
/// Apply changes to the config after it's been loaded, e.g. overriding
/// values in a config file using command-line options.
///
/// This can be safely deleted if you don't want to override config
/// settings from command-line options.
fn process_config(&self, mut config: Config) -> Result<Config, FrameworkError> {
// Alter the memo for all chains to include a suffix with Hermes build details
let web = "https://hermes.informal.systems";
let suffix = format!("{} {} ({})", CliCmd::name(), clap::crate_version!(), web);
for ccfg in config.chains.iter_mut() {
#[allow(irrefutable_let_patterns)]
if let ChainConfig::CosmosSdk(ref mut cosmos_ccfg) = ccfg {
if let Some(memo) = &cosmos_ccfg.memo_overwrite {
cosmos_ccfg.memo_prefix = memo.clone();
} else {
cosmos_ccfg.memo_prefix.apply_suffix(&suffix);
}
}
}
// For all commands except for `start` Hermes retries
// for a prolonged period of time.
if !matches!(self, CliCmd::Start(_)) {
for c in config.chains.iter_mut() {
#[allow(irrefutable_let_patterns)]
if let ChainConfig::CosmosSdk(ref mut cosmos_ccfg) = c {
cosmos_ccfg.rpc_timeout = Duration::from_secs(120);
}
}
}
match self {
CliCmd::Tx(cmd) => cmd.override_config(config),
CliCmd::Fee(cmd) => cmd.override_config(config),
// CliCmd::Help(cmd) => cmd.override_config(config),
// CliCmd::Keys(cmd) => cmd.override_config(config),
// CliCmd::Create(cmd) => cmd.override_config(config),
// CliCmd::Update(cmd) => cmd.override_config(config),
// CliCmd::Upgrade(cmd) => cmd.override_config(config),
// CliCmd::Start(cmd) => cmd.override_config(config),
// CliCmd::Query(cmd) => cmd.override_config(config),
// CliCmd::Listen(cmd) => cmd.override_config(config),
// CliCmd::Misbehaviour(cmd) => cmd.override_config(config),
// CliCmd::Version(cmd) => cmd.override_config(config),
_ => Ok(config),
}
}
}