Hermes Guide (v1.0.0-rc.2)
Hermes is a an open-source Rust implementation of a relayer for the Inter-Blockchain Communication protocol (IBC).
This guide can help you setup, configure, and operate Hermes to transfer packets between two or more IBC-enabled chains.
Sections
- Explains what Hermes is about.
- This section discusses what features to expect from Hermes, as well as a comparison between the Cosmos Go relayer and Hermes.
- The getting started section can help you setup, configure, and run Hermes.
- This section provides some tutorials on how to operate and test Hermes.
- The commands let you interact with Hermes using its command line interface.
- This part provides guidelines regarding troubleshooting and general resources for getting help.
- This section provides some definitions of terms used throughout the guide
Other References and Useful Links:
- Hermes Github repository — The official Github repository for Hermes.
- IBC Github repository
- The official repository for the Inter-blockchain protocol (IBC).
Disclaimer
This project is undergoing heavy development, use at your own risk.
What is Hermes?
Hermes is an open-source Rust implementation of a relayer for the Inter-Blockchain Communication protocol (IBC), released under the ibc-relayer-cli crate.
The Inter-Blockchain Communication protocol is an end-to-end, connection-oriented, stateful protocol for reliable, ordered, and authenticated communication between modules on separate distributed ledgers. 1
An IBC relayer is an off-chain process responsible for relaying IBC datagrams between any two chains. The way it does so is by scanning chain states, building transactions based on these states, and submitting the transactions to the chains involved in the network.
The relayer is a central element in the IBC network architecture. This is because chain modules in this architecture are not directly sending messages to each other over networking infrastructure, but instead they create and store the data to be retrieved and used by a relayer to build the IBC datagrams.
We sometimes refer to Hermes as "IBC Relayer CLI", to make it clear that this
is a relayer CLI (i.e., a binary) and distinguish it from the relayer core library
(that is the crate called ibc-relayer
).
Hermes is actively developed and maintained by Informal Systems in the ibc-rs repository.
Features
This section includes a summary of the supported and planned features. A feature matrix and comparison between the Rust and Go relayer implementations can be found in the Feature Matrix
Cosmos SDK compatibility: Hermes supports Cosmos SDK chains implementing the IBC v1.1 protocol specification. Cosmos SDK versions
0.41.3
to0.44.x
are officially supported. In case Hermes finds an incompatible SDK version, it will output a log warning.
Supported Features
- Basic features
- create and update clients
- refresh clients to prevent expiration
- establish connections with new or existing clients
- establish channels with new or existing connection
- channel closing handshake
- relay packets, acknowledgments, timeout and timeout-on-close packets, with zero or non-zero delay.
- queries for all objects
- Packet relaying over:
- multiple paths, for the chains in
config.toml
- multiple paths, for the chains in
- Restart support
- clear packets
- resume channel handshake if configured to relay
all
- resume connection handshake if configured to relay
all
- Client upgrade
- upgrading clients after a counterparty chain has performed an upgrade for IBC breaking changes
- Packet delay:
- establish path over non-zero delay connection
- relay all packets with the specified delay
- Monitor and submit misbehaviour for clients
- monitor client updates for misbehaviour (fork and BFT time violation)
- submit misbehaviour evidence to the on-chain IBC client.
misbehaviour submission to full node not yet supported
- Individual commands that build and send transactions for:
- creating and updating IBC Tendermint light clients
- sending connection open handshake datagrams
- sending channel open handshake datagrams
- sending channel closing handshake datagrams
- initiating a cross chain transfer (mainly for testing)
- relaying sent packets, acknowledgments and timeouts
- client upgrade
- Channel handshake for existing channel that is not in
Open
state - Connection handshake for existing connection that is not in
Open
state - Telemetry support
Upcoming / Unsupported Features
Planned features:
- Full Passive mode: relay from all IBC events
- Connection handshake for existing connection that is not in
Open
state
- Connection handshake for existing connection that is not in
- Relayer support for management application (add RPC server)
- Dynamic configuration management
Not planned:
- Relayer management application
- Create clients with user chosen parameters (such as UpgradePath)
- Use IBC light clients other than Tendermint such as Solo Machine
- Support non cosmos-SDK chains
Feature Matrix
This section gives more details about the features and implementation status of Hermes in comparison with the cosmos-go-relayer.
Legend:
Term | Description |
---|---|
❌ | feature not supported |
✅ | feature is supported |
Chain | chain related |
Cl | client related |
Conn | connection related |
Chan | channel related |
Cfg | config related |
.._Handshake_.. | can execute all transactions required to finish a handshake from a single command |
.._<msg>_A | building and sending msg from a command that scans chain state |
.._<msg>_P | building and sending msg from IBC event; doesn't apply to .._Init and FT_Transfer features |
Feature comparison between Hermes and the Go relayer
Features \ Status | Hermes | Cosmos Go | Feature Details |
---|---|---|---|
Restart | ✅ | ✅ | replays any IBC events that happened before restart |
Multiple_Paths | ✅ | ✅ | relays on multiple paths concurrently |
Connection Delay | ✅ | ❌ | |
Cl_Misbehavior | ✅ | ❌ | monitors and submits IBC client misbehavior |
Cl_Refresh | ✅ | ❌ | periodically refresh an on-chain client to prevent expiration |
Packet Delay | ✅ | ❌ | |
Chan_Unordered | ✅ | ✅ | |
Chan_Ordered | ✅ | ❓ | |
Cl_Tendermint_Create | ✅ | ✅ | tendermint light client creation |
Cl_Tendermint_Update | ✅ | ✅ | tendermint light client update |
Cl_Tendermint_Upgrade | ✅ | ✅ | tendermint light client upgrade |
Conn_Open_Handshake_A | ✅ | ✅ | |
Conn_Open_Handshake_P | ✅ | ❌ | |
Chan_Open_Handshake_A | ✅ | ✅ | |
Chan_Open_Handshake_P | ✅ | ❌ | |
Chan_Open_Handshake_Optimistic | ❌ | ❌ | open a channel on a non-Open connection |
Chan_Close_Handshake_P | ✅ | ✅ | |
Chan_Close_Handshake_A | ✅ | ❌ | |
FT_Transfer | ✅ | ✅ | can submit an ICS-20 fungible token transfer message |
ICA_Relay | ✅ | ❌ | can relay ICS-27 Interchain account packets |
Packet_Recv_A | ✅ | ✅ | |
Packet_Recv_P | ✅ | ✅ | |
Packet_Timeout_A | ✅ | ✅ | |
Packet_Timeout_P | ✅ | ✅ | |
Packet_TimeoutClose_A | ✅ | ❓ | |
Packet_TimeoutClose_P | ✅ | ❓ | |
Packet_Optimistic | ❌ | ❓ | relay packets over non-Open channels |
Cl_Non_Tendermint | ❌ | ❌ | supports non tendermint IBC light clients |
Chain_Non_Cosmos | ❌ | ❌ | supports non cosmos-SDK chains |
Cfg_Static | ✅ | ✅ | provides means for configuration prior to being started |
Cfg_Dynamic | ❌ | ❌ | provides means for configuration and monitoring during runtime |
Cfg_Download_Config | ❌ | ✅ | provides means for downloading recommended configuration |
Cfg_Edit_Config | ❌ | ✅ | provides means for editing the configuration from the CLI |
Cfg_Validation | ✅ | ✅ | provides means to validate the current configuration |
Telemetry | ✅ | ❌ | telemetry server to collect metrics |
REST API | ✅ | ❌ | REST API to interact with the relayer |
Getting Started
In order to run Hermes, please make sure you have all the pre-requisites installed on your machine.
Once you have these pre-requisites, you can build and run Hermes.
The instructions in this guide have been tested on
Linux
andMacOS
environments. Most of the commands should work on both environments. Even though you can build and run the relayer onWindows
(since we develop it in Rust and it supports cross platform compilation) we have not tested the relayer onWindows
and we do not support this operating system at this time.
Pre-requisites
1. Rust
The IBC Relayer is developed with the Rust programming language. In order to build and run the relayer you need to install and configure Rust
on your machine.
Fresh Rust installation
For instructions on how to install Rust
on your machine please follow the official Notes about Rust Installation
.
The provided instructions will install all the Rust toolchain including rustc
, cargo
, and rustup
that are required to build the project.
Version requirements
Hermes is developed and tested using the latest version of Rust, 1.60
at
the moment. To check that your toolchain is up-to-date run:
rustc --version
In case you already had installed the Rust toolchain in the past, you can
update your installation by running rustup update
.
Testing the installation
After you install the Rust
toolchain you can execute the following command:
cargo version
This should display the cargo
version and confirm the proper installation.
2. Golang
You will also need the Go programming language installed and configured on your machine. This is a requirement for the the section Installing Gaia in the Two Local Chains tutorial.
To install and configure Golang on your machine please follow the Golang official documentation.
Next Steps
Next, go to the Installation section to learn how to build Hermes.
Install the relayer
There are two main approaches for obtaining Hermes:
-
Installation:
- If you are running on a Unix machine (Linux/MacOS), then the simplest option is to download the latest binary.
- You can also install via Cargo.
-
Alternatively, build Hermes directly from source.
Install by downloading
Simply head to the GitHub Releases page and download the latest version of Hermes binary matching your platform:
- MacOS:
hermes-v1.0.0-rc.2-x86_64-apple-darwin.tar.gz
(or .zip), - Linux:
hermes-v1.0.0-rc.2-x86_64-unknown-linux-gnu.tar.gz
(or .zip).
The step-by-step instruction below should carry you through the whole process:
-
Make the directory where we'll place the binary:
mkdir -p $HOME/.hermes/bin
-
Extract the binary archive:
tar -C $HOME/.hermes/bin/ -vxzf $ARCHIVE_NAME
-
Update your path, by adding this line in your
.bashrc
or.zshrc
shell configuration file:export PATH="$HOME/.hermes/bin:$PATH"
NOTE: The binary may be initially prevented from running if you're on MacOS. See the "Open Anyway" instructions from this support forum if that is the case.
You should now be able to run Hermes by invoking the hermes
executable.
hermes version
hermes v1.0.0-rc.2
Install via Cargo
NOTE: This approach assumes you have installed all the pre-requisites on your machine.
Hermes is packaged in the ibc-relayer-cli
Rust crate.
To install the latest release of Hermes, run the following command in a terminal:
cargo install ibc-relayer-cli@1.0.0-rc.2 --bin hermes --locked
This will download and build the crate ibc-relayer-cli
, and install the
hermes
binary in $HOME/.cargo/bin
.
If you have not installed Rust and Cargo via rustup.rs, you may need to add the
$HOME/.cargo/bin
directory to yourPATH
environment variable. For most shells, this can be done by adding the following line to your.bashrc
or.zshrc
configuration file:export PATH="$HOME/.cargo/bin:$PATH"
You should now be able to run Hermes by invoking the hermes
executable.
hermes version
hermes v1.0.0-rc.2
Build from source
Clone the repository
Open a terminal and clone the ibc-rs
repository:
git clone https://github.com/informalsystems/ibc-rs.git
Change to the repository directory
cd ibc-rs
Checkout the latest release
Go to the ibc-rs releases page to see what is the most recent release.
Then checkout the release, for example if the most recent release is v1.0.0-rc.2
then execute the command:
git checkout v1.0.0-rc.2
Building with cargo build
This command builds all the crates from the ibc-rs
repository, namely: the ibc
modules crate, ibc-relayer
crate, ibc-proto
crate, and the ibc-relayer-cli
crate.
The last of these crates contains the hermes
binary.
cargo build --release --bin hermes
By default, Hermes bundles a telemetry service and server. To build Hermes without telemetry support, and get a smaller executable, supply the
--no-default-features flag
tocargo build
:cargo build --release --no-default-features --bin hermes
If the build is successful, the hermes
executable will be located in the following location:
./target/release/hermes
Troubleshooting:
In case the cargo build
command above fails, as a first course of action we
recommend trying to run the same command with the additional locked
flag:
cargo build --release --bin hermes --locked
Running for the first time
If you run the hermes
without any additional parameters you should see the usage and help information:
./target/release/hermes
hermes v1.0.0-rc.2
Informal Systems <hello@informal.systems>
USAGE:
hermes [OPTIONS] <SUBCOMMAND>
OPTIONS:
--config <CONFIG> Path to configuration file
-h, --help Print help information
--json Enable JSON output
-V, --version Print version information
SUBCOMMANDS:
clear Clear objects, such as outstanding packets on a channel
config Validate Hermes configuration file
create Create objects (client, connection, or channel) on chains
health-check Performs a health check of all chains in the the config
help Print this message or the help of the given subcommand(s)
keys Manage keys in the relayer for each chain
listen Listen to and display IBC events emitted by a chain
misbehaviour Listen to client update IBC events and handles misbehaviour
query Query objects from the chain
start Start the relayer in multi-chain mode
tx Create and send IBC transactions
update Update objects (clients) on chains
upgrade Upgrade objects (clients) after chain upgrade
completions Generate auto-complete scripts for different shells
Creating an alias for the executable
It might be easier to create an alias for hermes
so you can just run it by specifying the executable name instead of the whole path. In order to create an alias execute the following command:
alias hermes='cargo run --release --bin hermes --'
Shell auto-completions
The completions
subcommand of Hermes can be used to output a completion script
for a choice of widely used command-line shells.
Refer to hermes completions --help
for the list. Some shell-specific examples
of setting up auto-completion with this command are provided below; check your
shell configuration to decide on the suitable directory in which to install the script
and any further necessary modifications to the shell's startup files.
Bash
hermes completions --shell bash > ~/.local/share/bash-completion/completions/hermes
On a MacOS installation with Homebrew bash-completion
formula installed, use
hermes completions --shell bash > $(brew --prefix)/etc/bash_completion.d/hermes.bash-completion
Zsh
hermes completions --shell zsh > ~/.zfunc/_hermes
To make the shell load the script on initialization, add the directory to fpath
in your ~/.zshrc
before compinit
:
fpath+=~/.zfunc
Next Steps
Go to the Configuration
section to learn how to create a configuration file to be used by Hermes.
Configuration
In order to run Hermes, you will need to have a configuration file.
The format supported for the configuration file is TOML.
By default, Hermes expects the configuration file to be located at $HOME/.hermes/config.toml
.
This can be overridden by supplying the --config
flag when invoking hermes
, before the
name of the command to run, eg. hermes --config my_config.toml query connection channels --chain ibc-1 --connection connection-1
.
The current version of Hermes does not support managing the configuration file programmatically. You will need to use a text editor to create the file and add content to it.
hermes [--config CONFIG_FILE] COMMAND
Table of contents
Configuration
The configuration file must have one global
section, and one chains
section for each chain.
Note: As of 0.6.0, the Hermes configuration file is self-documented. Please read the configuration file
config.toml
itself for the most up-to-date documentation of parameters.
By default, Hermes will relay on all channels available between all the configured chains. In this way, every configured chain will act as a source (in the sense that Hermes listens for events) and as a destination (to relay packets that others chains have sent).
For example, if there are only two chains configured, then Hermes will only relay packets between those two, i.e. the two chains will serve as a source for each other, and likewise as a destination for each other's relevant events. Hermes will ignore all events that pertain to chains which are unknown (ie. not present in config.toml).
To restrict relaying on specific channels, or uni-directionally, you can use packet filtering policies.
Adding private keys
For each chain configured you need to add a private key for that chain in order to submit transactions, please refer to the Keys sections in order to learn how to add the private keys that are used by the relayer.
Connecting via TLS
Hermes supports connection via TLS for use-cases such as connecting from behind
a proxy or a load balancer. In order to enable this, you'll want to set the
rpc_addr
, grpc_addr
, or websocket_addr
parameters to specify a TLS
connection via HTTPS using the following scheme (note that the port number 443
is just used for example):
rpc_addr = 'https://domain.com:443'
grpc_addr = 'https://domain.com:443'
websocket_addr = 'wss://domain.com:443/websocket'
Support for Interchain Accounts
As of version 0.13.0, Hermes supports relaying on Interchain Accounts channels.
If the packet_filter
option in the chain configuration is disabled, then
Hermes will relay on all existing and future channels, including ICA channels.
There are two kinds of ICA channels:
- The host channels, whose port is
icahost
- The controller channels, whose port starts with
icacontroller-
followed by the owner account address. See the spec for more details.
If you wish to only relay on a few specific standard channels (here channel-0
and channel-1
),
but also relay on all ICA channels, you can specify the following packet filter:
Note the use of wildcards in the port and channel identifiers (
['ica*', '*']
) to match over all the possible ICA ports.
[chains.packet_filter]
policy = 'allow'
list = [
['ica*', '*'], # allow relaying on all channels whose port starts with `ica`
['transfer', 'channel-0'],
['transfer', 'channel-1'],
# Add any other port/channel pairs you wish to relay on
]
If you wish to relay on all channels but not on ICA channels, you can use the following packet filter configuration:
[chains.packet_filter]
policy = 'deny'
list = [
['ica*', '*'], # deny relaying on all channels whose port starts with `ica`
]
Next steps
Now that you learned how to build the relayer and how to create a configuration file, you can go to the Two Chains
tutorial to learn how to perform some local testing connecting the relayer to two local chains.
Example Configuration File
Here is a full example of a configuration file with two chains configured:
# The global section has parameters that apply globally to the relayer operation.
[global]
# Specify the verbosity for the relayer logging output. Default: 'info'
# Valid options are 'error', 'warn', 'info', 'debug', 'trace'.
log_level = 'info'
# Specify the mode to be used by the relayer. [Required]
[mode]
# Specify the client mode.
[mode.clients]
# Whether or not to enable the client workers. [Required]
enabled = true
# Whether or not to enable periodic refresh of clients. [Default: true]
# This feature only applies to clients that underlie an open channel.
# For Tendermint clients, the frequency at which Hermes refreshes them is 2/3 of their
# trusting period (e.g., refresh every ~9 days if the trusting period is 14 days).
# Note: Even if this is disabled, clients will be refreshed automatically if
# there is activity on a connection or channel they are involved with.
refresh = true
# Whether or not to enable misbehaviour detection for clients. [Default: false]
misbehaviour = true
# Specify the connections mode.
[mode.connections]
# Whether or not to enable the connection workers for handshake completion. [Required]
enabled = false
# Specify the channels mode.
[mode.channels]
# Whether or not to enable the channel workers for handshake completion. [Required]
enabled = false
# Specify the packets mode.
[mode.packets]
# Whether or not to enable the packet workers. [Required]
enabled = true
# Parametrize the periodic packet clearing feature.
# Interval (in number of blocks) at which pending packets
# should be periodically cleared. A value of '0' will disable
# periodic packet clearing. [Default: 100]
clear_interval = 100
# Whether or not to clear packets on start. [Default: false]
clear_on_start = true
# Toggle the transaction confirmation mechanism.
# The tx confirmation mechanism periodically queries the `/tx_search` RPC
# endpoint to check that previously-submitted transactions
# (to any chain in this config file) have been successfully delivered.
# If they have not been, and `clear_interval = 0`, then those packets are
# queued up for re-submission.
# Experimental feature. If set to `false`, the following telemetry metrics will be disabled:
# `ibc_acknowledgment_packets`, `ibc_receive_packets` and `ibc_timeout_packets`.
# [Default: false]
tx_confirmation = false
# The REST section defines parameters for Hermes' built-in RESTful API.
# https://hermes.informal.systems/rest.html
[rest]
# Whether or not to enable the REST service. Default: false
enabled = true
# Specify the IPv4/6 host over which the built-in HTTP server will serve the RESTful
# API requests. Default: 127.0.0.1
host = '127.0.0.1'
# Specify the port over which the built-in HTTP server will serve the restful API
# requests. Default: 3000
port = 3000
# The telemetry section defines parameters for Hermes' built-in telemetry capabilities.
# https://hermes.informal.systems/telemetry.html
[telemetry]
# Whether or not to enable the telemetry service. Default: false
enabled = true
# Specify the IPv4/6 host over which the built-in HTTP server will serve the metrics
# gathered by the telemetry service. Default: 127.0.0.1
host = '127.0.0.1'
# Specify the port over which the built-in HTTP server will serve the metrics gathered
# by the telemetry service. Default: 3001
port = 3001
# A chains section includes parameters related to a chain and the full node to which
# the relayer can send transactions and queries.
[[chains]]
# Specify the chain ID. Required
id = 'ibc-0'
# Specify the RPC address and port where the chain RPC server listens on. Required
rpc_addr = 'http://127.0.0.1:26657'
# Specify the GRPC address and port where the chain GRPC server listens on. Required
grpc_addr = 'http://127.0.0.1:9090'
# Specify the WebSocket address and port where the chain WebSocket server
# listens on. Required
websocket_addr = 'ws://127.0.0.1:26657/websocket'
# Specify the maximum amount of time (duration) that the RPC requests should
# take before timing out. Default: 10s (10 seconds)
# Note: Hermes uses this parameter _only_ in `start` mode; for all other CLIs,
# Hermes uses a large preconfigured timeout (on the order of minutes).
rpc_timeout = '10s'
# Specify the prefix used by the chain. Required
account_prefix = 'cosmos'
# Specify the name of the private key to use for signing transactions. Required
# See the Adding Keys chapter for more information about managing signing keys:
# https://hermes.informal.systems/commands/keys/index.html#adding-keys
key_name = 'testkey'
# Specify the address type which determines:
# 1) address derivation;
# 2) how to retrieve and decode accounts and pubkeys;
# 3) the message signing method.
# The current configuration options are for Cosmos SDK and Ethermint.
#
# Example configuration for chains based on Ethermint library:
#
# address_type = { derivation = 'ethermint', proto_type = { pk_type = '/ethermint.crypto.v1.ethsecp256k1.PubKey' } }
#
# Default: { derivation = 'cosmos' }, i.e. address derivation as in Cosmos SDK.
# Warning: This is an advanced feature! Modify with caution.
address_type = { derivation = 'cosmos' }
# Specify the store prefix used by the on-chain IBC modules. Required
# Recommended value for Cosmos SDK: 'ibc'
store_prefix = 'ibc'
# Specify the default amount of gas to be used in case the tx simulation fails,
# and Hermes cannot estimate the amount of gas needed.
# Default: 100 000
default_gas = 100000
# Specify the maximum amount of gas to be used as the gas limit for a transaction.
# If `default_gas` is unspecified, then `max_gas` will be used as `default_gas`.
# Default: 400 000
max_gas = 400000
# Specify the price per gas used of the fee to submit a transaction and
# the denomination of the fee. Required
gas_price = { price = 0.001, denom = 'stake' }
# Multiply this amount with the gas estimate, used to compute the fee
# and account for potential estimation error.
#
# Example: With this setting set to 1.1, then if the estimated gas
# is 80_000, then gas used to compute the fee will be adjusted to
# 80_000 * 1.1 = 88_000.
#
# Default: 1.1, ie. the gas is increased by 10%
# Minimum value: 1.0
gas_multiplier = 1.1
# Specify how many IBC messages at most to include in a single transaction.
# Default: 30
max_msg_num = 30
# Specify the maximum size, in bytes, of each transaction that Hermes will submit.
# Default: 2097152 (2 MiB)
max_tx_size = 2097152
# Specify the maximum amount of time to tolerate a clock drift.
# The clock drift parameter defines how much new (untrusted) header's time
# can drift into the future. Default: 5s
clock_drift = '5s'
# Specify the maximum time per block for this chain.
# The block time together with the clock drift are added to the source drift to estimate
# the maximum clock drift when creating a client on this chain. Default: 30s
# For cosmos-SDK chains a good approximation is `timeout_propose` + `timeout_commit`
# Note: This MUST be the same as the `max_expected_time_per_block` genesis parameter for Tendermint chains.
max_block_time = '30s'
# Specify the amount of time to be used as the light client trusting period.
# It should be significantly less than the unbonding period
# (e.g. unbonding period = 3 weeks, trusting period = 2 weeks).
# Default: 2/3 of the `unbonding period` for Cosmos SDK chains
trusting_period = '14days'
# Specify the trust threshold for the light client, ie. the maximum fraction of validators
# which have changed between two blocks.
# Default: { numerator = '1', denominator = '3' }, ie. 1/3.
# Warning: This is an advanced feature! Modify with caution.
trust_threshold = { numerator = '1', denominator = '3' }
# Specify a string that Hermes will use as a memo for each transaction it submits
# to this chain. The string is limited to 50 characters. Default: '' (empty).
# Note: Hermes will append to the string defined here additional
# operational debugging information, e.g., relayer build version.
memo_prefix = ''
# This section specifies the filters for policy based relaying.
#
# Default: no policy / filters, allow all packets on all channels.
#
# Only packet filtering based on channel identifier can be specified.
# A channel filter has two fields:
# 1. `policy` - one of two types are supported:
# - 'allow': permit relaying _only on_ the port/channel id in the list below,
# - 'deny': permit relaying on any channel _except for_ the list below.
# 2. `list` - the list of channels specified by the port and channel identifiers.
# Optionally, each element may also contains wildcards, for eg. 'ica*'
# to match all identifiers starting with 'ica' or '*' to match all identifiers.
#
# Example configuration of a channel filter, only allowing packet relaying on
# channel with port ID 'transfer' and channel ID 'channel-0', as well as on
# all ICA channels.
#
# [chains.packet_filter]
# policy = 'allow'
# list = [
# ['ica*', '*'],
# ['transfer', 'channel-0'],
# ]
# Specify that the transaction fees should be payed from this fee granter's account.
# Optional. If unspecified (the default behavior), then no fee granter is used, and
# the account specified in `key_name` will pay the tx fees for all transactions
# submitted to this chain.
# fee_granter = ''
[[chains]]
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'
default_gas = 100000
max_gas = 400000
gas_price = { price = 0.001, denom = 'stake' }
gas_multiplier = 1.1
max_msg_num = 30
max_tx_size = 2097152
clock_drift = '5s'
max_block_time = '30s'
trusting_period = '14days'
trust_threshold = { numerator = '1', denominator = '3' }
address_type = { derivation = 'cosmos' }
Telemetry
Since version 0.4.0.
To gain a better understanding of the status and activity of the relayer, Hermes features a built-in telemetry service based on the OpenTelemetry observability framework, whose metrics can be exposed over HTTP for integration with the Prometheus monitoring system.
The official Hermes builds for Linux and macOS come with telemetry support since version 0.4.0, and can be downloaded directly from the GitHub Releases page.
Configuration
The telemetry service is not active by default, and must be enabled in the relayer configuration:
[telemetry]
enabled = true
host = '127.0.0.1'
port = 3001
Please see the relevant section in the Configuration page for details about the configuration options.
Metrics
The following table describes the metrics currently tracked by the telemetry service:
Name | Description | OpenTelemetry type |
---|---|---|
workers | Number of workers per object | i64 UpDownCounter |
ibc_client_updates | Number of client updates performed per client | u64 Counter |
ibc_client_misbehaviours | Number of misbehaviours detected per client | u64 Counter |
ibc_receive_packets | Number of confirmed receive packets relayed per channel. Available if relayer runs with Tx confirmation enabled | u64 Counter |
ibc_acknowledgment_packets | Number of confirmed acknowledgment packets relayed per channel. Available if relayer runs with Tx confirmation enabled | u64 Counter |
ibc_timeout_packets | Number of confirmed timeout packets relayed per channel. Available if relayer runs with Tx confirmation enabled | u64 Counter |
wallet_balance | The balance of each wallet Hermes uses per chain. Please note that when converting the balance to f64 a loss in | |
precision might be introduced in the displayed value | f64 ValueRecorder | |
ws_events | How many IBC events did Hermes receive via the websocket subscription, in total since starting up, per chain. | Counter |
ws_reconnect | Number of times Hermes had to reconnect to the WebSocket endpoint | Counter |
tx_latency_submitted | Latency for all transactions submitted to a chain (i.e., difference between the moment when Hermes received an event until the corresponding transaction(s) were submitted). | u64 ValueRecorder |
tx_latency_confirmed | Latency for all transactions confirmed by a chain (i.e., difference between the moment when Hermes received an event until the corresponding transaction(s) were confirmed). Requires tx_confirmation = true . | u64 ValueRecorder |
msg_num | How many messages Hermes submitted to a specific chain. | u64 Counter |
queries | Number of queries emitted by the relayer, per chain and query type | u64 Counter |
query_cache_hits | Number of cache hits for queries emitted by the relayer, per chain and query type | u64 Counter |
send_packet_count | Number of SendPacket events processed | u64 Counter |
acknowledgement_count | Number of WriteAcknowledgement events processed | u64 Counter |
cleared_send_packet_count | Number of SendPacket events processed during the initial and periodic clearing | u64 Counter |
cleared_acknowledgment_count | Number of WriteAcknowledgement events processed during the initial and periodic clearing | u64 Counter |
backlog_oldest_sequence | Sequence number of the oldest pending packet in the backlog, per channel | u64 ValueRecorder |
backlog_oldest_timestamp | Local timestamp for the oldest pending packet in the backlog, per channel | u64 ValueRecorder |
backlog_size | Total number of pending packets, per channel | u64 ValueRecorder |
Integration with Prometheus
With the settings , the telemetry service will be enabled and will serve the metrics using
the Prometheus encoder over HTTP at http://localhost:3001/metrics
.
After starting Hermes with hermes start
, and letting it run for a while to relay packets,
open http://localhost:3001/metrics
in a browser, you should
see Prometheus-encoded metrics.
For example, with two channels and after transferring some tokens between the chains:
# HELP acknowledgement_count Number of WriteAcknowledgement events processed
# TYPE acknowledgement_count counter
acknowledgement_count{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer"} 25
# HELP cache_hits Number of cache hits for queries emitted by the relayer, per chain and query type
# TYPE cache_hits counter
cache_hits{chain="ibc-0",query_type="query_channel"} 63
cache_hits{chain="ibc-0",query_type="query_client_state"} 25
cache_hits{chain="ibc-0",query_type="query_connection"} 19
cache_hits{chain="ibc-1",query_type="query_channel"} 28
cache_hits{chain="ibc-1",query_type="query_client_state"} 36
cache_hits{chain="ibc-1",query_type="query_connection"} 29
# HELP cleared_acknowledgment_count Number of WriteAcknowledgment events processed during the initial and periodic clearing
# TYPE cleared_acknowledgment_count counter
cleared_acknowledgment_count{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer"} 20
# HELP cleared_send_packet_count Number of SendPacket events processed during the initial and periodic clearing
# TYPE cleared_send_packet_count counter
cleared_send_packet_count{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer"} 10
# HELP ibc_acknowledgment_packets Number of confirmed acknowledgment packets relayed per channel. Available if relayer runs with Tx confirmation enabled
# TYPE ibc_acknowledgment_packets counter
ibc_acknowledgment_packets{src_chain="ibc-1",src_channel="channel-0",src_port="transfer"} 30
# HELP ibc_receive_packets Number of confirmed receive packets relayed per channel. Available if relayer runs with Tx confirmation enabled
# TYPE ibc_receive_packets counter
ibc_receive_packets{src_chain="ibc-0",src_channel="channel-0",src_port="transfer"} 25
# HELP msg_num How many messages Hermes submitted to the chain, per chain
# TYPE msg_num counter
msg_num{chain="ibc-0"} 48
msg_num{chain="ibc-1"} 27
# HELP oldest_sequence The sequence number of the oldest SendPacket event observed without its corresponding WriteAcknowledgement event. If this value is 0, it means Hermes observed a WriteAcknowledgment event for all the SendPacket events
# TYPE oldest_sequence gauge
oldest_sequence{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer"} 0
# HELP oldest_timestamp The timestamp of the oldest sequence number in seconds
# TYPE oldest_timestamp gauge
oldest_timestamp{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer"} 0
# HELP queries Number of queries emitted by the relayer, per chain and query type
# TYPE queries counter
queries{chain="ibc-0",query_type="query_application_status"} 71
queries{chain="ibc-0",query_type="query_channel"} 1
queries{chain="ibc-0",query_type="query_client_connections"} 1
queries{chain="ibc-0",query_type="query_client_state"} 58
queries{chain="ibc-0",query_type="query_clients"} 1
queries{chain="ibc-0",query_type="query_connection"} 1
queries{chain="ibc-0",query_type="query_connection_channels"} 1
queries{chain="ibc-0",query_type="query_consensus_state"} 61
queries{chain="ibc-0",query_type="query_consensus_states"} 1
queries{chain="ibc-0",query_type="query_latest_height"} 1
queries{chain="ibc-0",query_type="query_packet_acknowledgements"} 1
queries{chain="ibc-0",query_type="query_packet_commitments"} 4
queries{chain="ibc-0",query_type="query_staking_params"} 2
queries{chain="ibc-0",query_type="query_txs"} 32
queries{chain="ibc-0",query_type="query_unreceived_acknowledgements"} 92
queries{chain="ibc-1",query_type="query_application_status"} 70
queries{chain="ibc-1",query_type="query_channel"} 51
queries{chain="ibc-1",query_type="query_client_connections"} 1
queries{chain="ibc-1",query_type="query_client_state"} 56
queries{chain="ibc-1",query_type="query_clients"} 1
queries{chain="ibc-1",query_type="query_connection"} 1
queries{chain="ibc-1",query_type="query_connection_channels"} 1
queries{chain="ibc-1",query_type="query_consensus_state"} 60
queries{chain="ibc-1",query_type="query_consensus_states"} 1
queries{chain="ibc-1",query_type="query_latest_height"} 1
queries{chain="ibc-1",query_type="query_packet_acknowledgements"} 2
queries{chain="ibc-1",query_type="query_packet_commitments"} 3
queries{chain="ibc-1",query_type="query_staking_params"} 2
queries{chain="ibc-1",query_type="query_txs"} 38
queries{chain="ibc-1",query_type="query_unreceived_packets"} 52
# HELP send_packet_count Number of SendPacket events processed
# TYPE send_packet_count counter
send_packet_count{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer"} 25
# HELP tx_latency_confirmed The latency for all transactions submitted & confirmed to a specific chain, i.e. the difference between the moment when Hermes received a batch of events until the corresponding transaction(s) were confirmed. Milliseconds.
# TYPE tx_latency_confirmed histogram
tx_latency_confirmed_bucket{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer",le="1000"} 0
tx_latency_confirmed_bucket{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer",le="5000"} 2
tx_latency_confirmed_bucket{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer",le="9000"} 2
tx_latency_confirmed_bucket{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer",le="13000"} 2
tx_latency_confirmed_bucket{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer",le="17000"} 2
tx_latency_confirmed_bucket{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer",le="20000"} 2
tx_latency_confirmed_bucket{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer",le="+Inf"} 2
tx_latency_confirmed_sum{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer"} 7518
tx_latency_confirmed_count{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer"} 2
tx_latency_confirmed_bucket{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer",le="1000"} 0
tx_latency_confirmed_bucket{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer",le="5000"} 2
tx_latency_confirmed_bucket{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer",le="9000"} 2
tx_latency_confirmed_bucket{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer",le="13000"} 2
tx_latency_confirmed_bucket{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer",le="17000"} 2
tx_latency_confirmed_bucket{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer",le="20000"} 2
tx_latency_confirmed_bucket{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer",le="+Inf"} 2
tx_latency_confirmed_sum{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer"} 5291
tx_latency_confirmed_count{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer"} 2
# HELP tx_latency_submitted The latency for all transactions submitted to a specific chain, i.e. the difference between the moment when Hermes received a batch of events and when it submitted the corresponding transaction(s). Milliseconds.
# TYPE tx_latency_submitted histogram
tx_latency_submitted_bucket{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer",le="200"} 0
tx_latency_submitted_bucket{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer",le="500"} 1
tx_latency_submitted_bucket{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer",le="1000"} 2
tx_latency_submitted_bucket{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer",le="2000"} 2
tx_latency_submitted_bucket{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer",le="5000"} 2
tx_latency_submitted_bucket{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer",le="10000"} 2
tx_latency_submitted_bucket{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer",le="+Inf"} 2
tx_latency_submitted_sum{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer"} 1175
tx_latency_submitted_count{chain="ibc-0",channel="channel-0",counterparty="ibc-1",port="transfer"} 2
tx_latency_submitted_bucket{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer",le="200"} 1
tx_latency_submitted_bucket{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer",le="500"} 2
tx_latency_submitted_bucket{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer",le="1000"} 2
tx_latency_submitted_bucket{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer",le="2000"} 2
tx_latency_submitted_bucket{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer",le="5000"} 2
tx_latency_submitted_bucket{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer",le="10000"} 2
tx_latency_submitted_bucket{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer",le="+Inf"} 2
tx_latency_submitted_sum{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer"} 542
tx_latency_submitted_count{chain="ibc-1",channel="channel-0",counterparty="ibc-0",port="transfer"} 2
# HELP wallet_balance The balance of each wallet Hermes uses per chain. Please note that when converting the balance to f64 a loss in precision might be introduced in the displayed value
# TYPE wallet_balance gauge
wallet_balance{account="cosmos1a9emd54sp0xsw77e2pzcc2pjfp7jvtl64p64w7",chain="ibc-1",denom="stake"} 99940782
wallet_balance{account="cosmos1s9jwwt60edxhy0ez84h3wfujerj5mgzhmasy23",chain="ibc-0",denom="stake"} 99962295
# HELP workers Number of workers per object
# TYPE workers gauge
workers{type="client"} 2
workers{type="packet"} 2
workers{type="wallet"} 2
# HELP ws_events How many IBC events did Hermes receive via the WebSocket subscription, per chain
# TYPE ws_events counter
ws_events{chain="ibc-0"} 57
ws_events{chain="ibc-1"} 37
REST API
Since version 0.7.0.
Hermes features a built-in HTTP server which exposes information about the relayer configuration and state via a REST API.
Table of Contents
Configuration
The REST API is not active by default, and must be enabled in the relayer configuration:
[rest]
enabled = true
host = '127.0.0.1'
port = 3000
Please see the relevant section in the Configuration page for details about the configuration options.
Endpoints
GET /version
This endpoint returns the version of the Hermes (under the ibc-relayer
key) as well
as the version of the REST server itself (under the ibc-relayer-rest
key).
Example
❯ curl -s -X GET 'http://127.0.0.1:3000/version' | jq
[
{
"name": "ibc-relayer",
"version": "v1.0.0-rc.2"
},
{
"name": "ibc-relayer-rest",
"version": "0.1.0"
}
]
GET /chains
This endpoint return the identifiers of the chains that Hermes is connected to.
Those identifiers can be used with the /chain/:id
endpoint to gather more
information about each chain's configuration. See the next section for more details.
Example
❯ curl -s -X GET 'http://127.0.0.1:3000/chains' | jq
{
"status": "success",
"result": [
"ibc-0",
"ibc-1"
]
}
GET /chain/:id
This endpoint returns the configuration of the chain with the given identifier,
where :id
stands for the identififer.
Example
❯ curl -s -X GET 'http://127.0.0.1:3000/chain/ibc-0' | jq
{
"status": "success",
"result": {
"id": "ibc-0",
"rpc_addr": "http://127.0.0.1:26657/",
"websocket_addr": "ws://127.0.0.1:26657/websocket",
"grpc_addr": "http://127.0.0.1:9090/",
"rpc_timeout": "10s",
"account_prefix": "cosmos",
"key_name": "testkey",
"store_prefix": "ibc",
"max_gas": 900000000,
"gas_multiplier": 1.0,
"max_msg_num": 60,
"max_tx_size": 2097152,
"clock_drift": "5s",
"trusting_period": "14days",
"trust_threshold": {
"numerator": "1",
"denominator": "3"
},
"gas_price": {
"price": 0.001,
"denom": "stake"
},
"packet_filter": {
"policy": "allowall"
}
}
}
GET /state
This endpoint returns the current state of the relayer, namely which chains it is connected to, as well as a description of all the workers which are currently active.
❯ curl -s -X GET 'http://127.0.0.1:3000/state' | jq
{
"status": "success",
"result": {
"chains": [
"ibc-0",
"ibc-1"
],
"workers": {
"Client": [
{
"id": 3,
"object": {
"type": "Client",
"dst_chain_id": "ibc-1",
"dst_client_id": "07-tendermint-0",
"src_chain_id": "ibc-0"
}
},
{
"id": 4,
"object": {
"type": "Client",
"dst_chain_id": "ibc-1",
"dst_client_id": "07-tendermint-1",
"src_chain_id": "ibc-0"
}
},
{
"id": 1,
"object": {
"type": "Client",
"dst_chain_id": "ibc-0",
"dst_client_id": "07-tendermint-0",
"src_chain_id": "ibc-1"
}
},
{
"id": 2,
"object": {
"type": "Client",
"dst_chain_id": "ibc-0",
"dst_client_id": "07-tendermint-1",
"src_chain_id": "ibc-1"
}
}
]
}
}
}
Tutorials
This section includes tutorials for some common relayer use cases and commands. You can also refer to the Commands Reference section to learn more about individual commands.
Basic tutorials
In this tutorial you will learn how to start two local Cosmos Gaia
chains that support the IBC
protocol and start relaying packets between them.
Tutorial: Relayer with two local chains
In this tutorial we will show how you can test the relayer against two chains using Gaiad manager gm
. This is the easiest way to get started.
Using gm
we will start two gaia
chains that support the IBC
protocol.
Follow the steps in this tutorial section starting with the Install Gaia section.
Install Gaia
For gm
to start the chains, it requires gaia to be installed.
NOTE: This assumes you have
Golang
programming language installed on your machine. If not, please ensure you install before proceeding. See more details in the Pre-requisites section.
Clone gaia
Clone the repository from Github:
git clone https://github.com/cosmos/gaia.git ~/go/src/github.com/cosmos/gaia
Build and Install
Run the make
command to build and install gaiad
cd ~/go/src/github.com/cosmos/gaia
git checkout <latest-release-tag>
make install
Find the latest-release-tag here.
NOTE: Specific to M1 MacOS, there could be some warnings after running
make install
which can be safely ignored as long asgaiad
binaries are built in$HOME/go/bin
directory.
Add the pathexport PATH=$HOME/go/bin:$PATH
If the command above is successful you can run the following command to ensure it was properly installed:
gaiad version --log_level error --long | head -n4
Output:
name: gaia
server_name: gaiad
version: v4.2.1
commit: dbd8a6fb522c571debf958837f9113c56d418f6b
Next Steps
In the next section you will learn how to start two local chains
Install Gaiad Manager
Gaiad manager (gm
) is a command-line tool (CLI) that helps manage local gaiad
networks.
Follow the instructions
Requirements
- Bourne shell (
sh
) sconfig
andstoml
installed in your PATH (put them in/usr/local/bin
)sed
,tr
- For shell-completion Bourne Again Shell (
bash
) for the local user
How to run
- Install the dependencies.
On MacOS:
# You might need sudo permissions and create the `usr/local/bin` directory
curl -Lo /usr/local/bin/sconfig https://github.com/freshautomations/sconfig/releases/download/v0.1.0/sconfig_darwin_amd64
curl -Lo /usr/local/bin/stoml https://github.com/freshautomations/stoml/releases/download/v0.7.0/stoml_darwin_amd64
chmod 755 /usr/local/bin/sconfig
chmod 755 /usr/local/bin/stoml
On Linux:
curl -Lo /usr/local/bin/sconfig https://github.com/freshautomations/sconfig/releases/download/v0.1.0/sconfig_linux_amd64
curl -Lo /usr/local/bin/stoml https://github.com/freshautomations/stoml/releases/download/v0.7.0/stoml_linux_amd64
chmod 755 /usr/local/bin/sconfig
chmod 755 /usr/local/bin/stoml
- Install
gm
git clone https://github.com/informal/ibc-rs
ibc-rs/scripts/gm/bin/gm install
Alternatively, you can create the folder $HOME/.gm/bin
and copy the files from scripts/gm/bin
in there.
- Activate
gm
- Add
source $HOME/.gm/bin/shell-support
to a file that executes when a new terminal window comes up ($HOME/.bash_profile
or$HOME/.bashrc
or$HOME/.zprofile
) - (Optional) Enable auto-completion On MacOS:
# Note: zsh is the default shell on MacOS, so no need to run this unless you explicitly use bash
brew install bash-completion
On Linux:
apt install bash-completion || yum install bash-completion
- Restart your terminal
Note: The shell-support
script allows bash-completion as well as creating a gm
alias, so you don't need to add more
entries to your PATH environment variable. If you don't want to use this, you can always just add $HOME/.gm/bin
to
your path.
The configuration: gm.toml
Where: $HOME/.gm/gm.toml
.
Description: This file contains all the high-level node configuration that gm
is aware of. Note that all entries under [global]
are also valid entries under any [node]
header, and can be used to override the global entries for specific nodes/validators.
Entries: All entries are defined and documented in the scripts/gm/gm.toml
example configuration file.
Copy and paste below to $HOME/.gm/gm.toml
The following configuration you need to specify 2 gaiad
chains. hermes
will know about these chains.
[global]
add_to_hermes = false
auto_maintain_config = true
extra_wallets = 2
gaiad_binary = "~/go/bin/gaiad"
hdpath = ""
home_dir = "$HOME/.gm"
ports_start_at = 27000
validator_mnemonic = ""
wallet_mnemonic = ""
[global.hermes]
binary = "./hermes" #change this path according to your setup
config = "$HOME/.hermes/config.toml"
log_level = "info"
telemetry_enabled = true
telemetry_host = "127.0.0.1"
telemetry_port = 3001
[ibc-0]
ports_start_at = 27010
[ibc-1]
ports_start_at = 27020
[node-0]
add_to_hermes = true
network = "ibc-0"
ports_start_at = 27030
[node-1]
add_to_hermes = true
network = "ibc-1"
ports_start_at = 27040
In the next section we will configure hermes and start local chains using gm
NOTE: Go to this page for more detils about Gaiad Manager
Start the local chains
In this chapter, you will learn how to spawn two Gaia chains, and use Hermes to relay packets between them.
To spawn the chains and configure Hermes accordingly, we will make use of Gaiad Manager gm
that we installed in the last section Install Gaiad Manager
Stop existing gaiad
processes
If this is not the first time you are running the script, you can manually stop the two gaia instances executing the following command to kill all gaiad
processes:
killall gaiad
NOTE: If you have any
Docker
containers running that might be using the same ports asgaiad
(e.g. port 26657 or port 9090), please ensure you stop them first before proceeding to the next step.
Configuration file
Gaiad Manager gm
takes care of creating the configuration file. Run the command below to create the $HOME/.hermes/config.toml
file
gm hermes config
Please check the Configuration
section for more information about the relayer configuration file.
Based on the gm.toml
we created in the previous section Install Gaiad Manager, your $HOME/.hermes/config.toml
file should look like below :
config.toml
[global]
log_level = 'info'
[mode]
[mode.clients]
enabled = true
refresh = true
misbehaviour = true
[mode.connections]
enabled = true
[mode.channels]
enabled = true
[mode.packets]
enabled = true
clear_interval = 100
clear_on_start = true
tx_confirmation = true
[telemetry]
enabled = true
host = '127.0.0.1'
port = 3001
[[chains]]
id = 'ibc-0'
rpc_addr = 'http://localhost:27030'
grpc_addr = 'http://localhost:27032'
websocket_addr = 'ws://localhost:27030/websocket'
rpc_timeout = '15s'
account_prefix = 'cosmos'
key_name = 'wallet'
store_prefix = 'ibc'
gas_price = { price = 0.01, denom = 'stake' }
max_gas = 10000000
clock_drift = '5s'
trusting_period = '14days'
trust_threshold = { numerator = '1', denominator = '3' }
[[chains]]
id = 'ibc-1'
rpc_addr = 'http://localhost:27040'
grpc_addr = 'http://localhost:27042'
websocket_addr = 'ws://localhost:27040/websocket'
rpc_timeout = '15s'
account_prefix = 'cosmos'
key_name = 'wallet'
store_prefix = 'ibc'
gas_price = { price = 0.01, denom = 'stake' }
max_gas = 10000000
clock_drift = '5s'
trusting_period = '14days'
trust_threshold = { numerator = '1', denominator = '3' }
Adding private keys to the chains
gm
will automatically generate private keys that will be used by hermes
to sign transactions.
To see the keys generated by gm
, run the command below
gm key
This will generate an output similar to the one below (albeit all on the same line):
{
"name": "testkey",
"type": "local",
"address": "cosmos1tc3vcuxyyac0dmayf887t95tdg7qpyql48w7gj",
"pubkey": "cosmospub1addwnpepqgg7ng4ycm60pdxfzdfh4hjvkwcr3da59mr8k883vsstx60ruv7kur4525u",
"mnemonic": "[24 words mnemonic]"
}
Next, we will need to associate a private key with chains ibc-0
and ibc-1
which hermes
will use to sign transactions.
gm hermes keys
If successful, the command should show an output similar to:
Success: Added key testkey ([ADDRESS]) on [CHAIN ID] chain
Starting the chains with gm
Make sure you have the $HOME/.gm/gm.toml
that we configured in the previous section Install Gaiad Manager and run the gm
command below to start the chains.
gm start
This configures and starts two gaiad
instances, one named ibc-0
and the other ibc-1
graph TD A[gm] -->|start| C(start chains) C -->|gaiad| D[ibc-0] C -->|gaiad| F[ibc-1]
If the command runs successfully you should see a message similar to the one below in the terminal:
network1 started, PID: 99468, LOG: /Users/testuser/.gm/network1/log
network2 started, PID: 99538, LOG: /Users/testuser/.gm/network2/log
ibc-0 started, PID: 99645, LOG: /Users/testuser/.gm/ibc-0/log
ibc-1 started, PID: 99750, LOG: /Users/testuser/.gm/ibc-1/log
Run the below command to check the status of the chains
gm status
$HOME/.gm directory
This directory is creted when you install gm
and the binaries are stored here but when we start the chains, all the related files and folders are stored here as well.
The $HOME/.gm directory has a tree structure similar to the one below:
.gm
├── bin
│ ├── gm
│ ├── lib-gm
│ └── shell-support
├── gm.toml
├── ibc-0
│ ├── config
│ ├── data
│ ├── init.json
│ ├── keyring-test
│ ├── log
│ ├── pid
│ ├── validator_seed.json
│ ├── wallet1_seed.json
│ ├── wallet2_seed.json
│ └── wallet_seed.json
├── ibc-1
│ ├── config
│ ├── data
│ ├── init.json
│ ├── keyring-test
│ ├── log
│ ├── pid
│ ├── validator_seed.json
│ ├── wallet1_seed.json
│ ├── wallet2_seed.json
│ └── wallet_seed.json
├── node-0
│ ├── config
│ ├── data
│ ├── init.json
│ ├── keyring-test
│ ├── log
│ └── pid
└── node-1
├── config
├── data
├── init.json
├── keyring-test
├── log
└── pid
Tip: You can use the command
tree ./data/ -L 2
to view the folder structure above
$HOME/.hermes directory
By the default hermes
expects the configuration file to be in the $HOME/.hermes
folder.
It also stores the private keys for each chain in this folder as outlined in the Keys section.
After executing gm start
, this is how the folder should look like:
$HOME/.hermes/
├── config.toml
└── keys
├── ibc-0
│ └── keyring-test
│ └── testkey.json
└── ibc-1
└── keyring-test
└── testkey.json
Next Steps
The next section describes how identifers for clients, connections and channels are allocated, and will walk you through how to pre-allocate some identifers to help matching them with their corresponding chains for the purpose of this tutorial.
Identifiers
A chain allocates identifiers when it creates clients, connections and channels. These identifiers can subsequently be used to refer to existing clients, connections and channels.
NOTE: If you want to ensure you get the same identifiers while following the tutorials, run the each of the three commands below once on
ibc-1
. This will ensure that when going through the tutorial, a second channel onibc-1
with identifierchannel-1
will created.
Chains allocate identifiers using a chain specific allocation scheme. Currently, cosmos-sdk implementation uses the follow identifiers:
1. Client Identifiers
07-tendermint-<n>
for tendermint clients
For example 07-tendermint-0
is assigned to the first client created on ibc-1
:
hermes create client --host-chain ibc-1 --reference-chain ibc-0
Success: CreateClient(
CreateClient(
Attributes {
height: Height {
revision: 1,
height: 103,
},
client_id: ClientId(
"07-tendermint-0",
),
client_type: Tendermint,
consensus_height: Height {
revision: 0,
height: 112,
},
},
),
)
We will create a second client on ibc-1
with identifier 07-tendermint-1
in the client tutorial.
2. Connection Identifiers
connection-<n>
for connections
For example connection-0
is assigned to the first connection created on ibc-1
:
hermes tx conn-init --b-chain ibc-1 --a-chain ibc-0 --b-client 07-tendermint-0 --a-client 07-tendermint-0
Success: OpenInitConnection(
OpenInit(
Attributes {
height: Height {
revision: 1,
height: 119,
},
connection_id: Some(
ConnectionId(
"connection-0",
),
),
client_id: ClientId(
"07-tendermint-0",
),
counterparty_connection_id: None,
counterparty_client_id: ClientId(
"07-tendermint-0",
),
},
),
)
We will create a second connection on ibc-1
with identifier connection-1
in the connection tutorial.
3. Channel Identifiers
channel-<n>
for channels
For example channel-0
is assigned to the first channel created on ibc-1
:
hermes tx chan-open-init --b-chain ibc-1 --a-chain ibc-0 --b-connection connection-0 --b-port transfer --a-port transfer
Success: OpenInitChannel(
OpenInit(
Attributes {
height: Height {
revision: 1,
height: 225,
},
port_id: PortId(
"transfer",
),
channel_id: Some(
ChannelId(
"channel-0",
),
),
connection_id: ConnectionId(
"connection-0",
),
counterparty_port_id: PortId(
"transfer",
),
counterparty_channel_id: None,
},
),
)
In the following tutorials the ibc-0
and ibc-1
chains are setup and configured.
For clarity, the tutorials run on a setup where the identifiers allocated to the client, connection and channel on ibc-0
are 07-tendermint-0
, connection-0
and channel-0
respectively. Identifiers allocated to the client, connection and channel on ibc-1
are 07-tendermint-1
, connection-1
and channel-1
respectively.
Before going over the next sections, please ensure the commands above are executed.
Next Steps
The following sections describe the commands to connect and relay packets between two chains. You can also use a simplified approach for managing relaying paths.
Connect the chains using relay paths
A relay path refers to a specific channel used to interconnect two chains and over which packets are being sent.
Hermes can be started to listen for packet events on the two ends of multiple paths and relay packets over these paths. This can be done over a new path or over existing paths.
Before proceeding to the sections above, please first, make sure you followed the steps in the Identifiers section
Create a new path
Perform client creation, connection and channel handshake to establish a new path between the transfer
ports on ibc-0
and ibc-1
chains.
hermes create channel --a-chain ibc-0 --b-chain ibc-1 --a-port transfer --b-port transfer --new-client-connection
If all the handshakes are performed successfully you should see a message similar to the one below:
Success: Channel {
ordering: Unordered,
a_side: ChannelSide {
chain: ProdChainHandle {
chain_id: ChainId {
id: "ibc-0",
version: 0,
},
runtime_sender: Sender { .. },
},
client_id: ClientId(
"07-tendermint-0",
),
connection_id: ConnectionId(
"connection-0",
),
port_id: PortId(
"transfer",
),
channel_id: ChannelId(
"channel-0",
),
},
b_side: ChannelSide {
chain: ProdChainHandle {
chain_id: ChainId {
id: "ibc-1",
version: 1,
},
runtime_sender: Sender { .. },
},
client_id: ClientId(
"07-tendermint-1",
),
connection_id: ConnectionId(
"connection-1",
),
port_id: PortId(
"transfer",
),
channel_id: ChannelId(
"channel-1",
),
},
connection_delay: 0s,
version: Some(
"ics20-1",
),
}
Note that for each side, a_side (ibc-0) and b_side (ibc-1) there are a client_id, connection_id, channel_id and port_id. With all these established, you have a path that you can relay packets over.
Relay packets on multiple paths
Hermes can relay packets over all current or future paths between the configured set of chains.
Follow the steps below to connect three chains together and relay packets between them:
-
Add the config for the third chain to the existing
$HOME/.gm/gm.toml
file[global] add_to_hermes = false auto_maintain_config = true extra_wallets = 2 gaiad_binary = "~/go/bin/gaiad" hdpath = "" home_dir = "$HOME/.gm" ports_start_at = 27000 validator_mnemonic = "" wallet_mnemonic = "" [global.hermes] binary = "$HOME/ibc-rs/target/debug/hermes" #change this path according to your setup config = "./hermes" log_level = "info" telemetry_enabled = true telemetry_host = "127.0.0.1" telemetry_port = 3001 [ibc-0] ports_start_at = 27010 [ibc-1] ports_start_at = 27020 [node-0] add_to_hermes = true network = "ibc-0" ports_start_at = 27030 [node-1] add_to_hermes = true network = "ibc-1" ports_start_at = 27040 [ibc-2] ports_start_at = 27050 [node-2] add_to_hermes = true network = "ibc-2" ports_start_at = 27060
-
Start the third chain
gm start
-
Update the
$HOME/.hermes/.config.toml
filegm hermes config
-
Associate the keys to the new chain
gm hermes keys
-
Check the status of the chains
gm status
-
Create a channel between
ibc-0
andibc-1
. Since this is the first time we're connecting these two chains, we'll need to spin up a client and a connection between them as well. Thecreate channel
command gives us the convenient option to create a client and a connection. Keep in mind that this is not the default behavior ofcreate channel
, but in this case we're making an exception. Execute the following command:hermes create channel --a-chain ibc-0 --b-chain ibc-1 --a-port transfer --b-port transfer --new-client-connection
Then respond 'yes' to the prompt that pops up. Once the command has run to completion, you should see the following among the output logs:
(...) Success: Channel { ordering: Unordered, a_side: ChannelSide { chain: ProdChainHandle { chain_id: ChainId { id: "ibc-0", version: 0, }, runtime_sender: Sender { .. }, }, client_id: ClientId( "07-tendermint-0", ), connection_id: ConnectionId( "connection-0", ), port_id: PortId( "transfer", ), channel_id: ChannelId( "channel-0", ), }, b_side: ChannelSide { chain: ProdChainHandle { chain_id: ChainId { id: "ibc-1", version: 1, }, runtime_sender: Sender { .. }, }, client_id: ClientId( "07-tendermint-0", ), connection_id: ConnectionId( "connection-0", ), port_id: PortId( "transfer", ), channel_id: ChannelId( "channel-0", ), }, connection_delay: 0s, version: Some( "ics20-1", ), }
Note that the channel identifier on both
ibc-0
andibc-1
ischannel-0
. -
Create a channel between
ibc-1
andibc-2
using the structure of the previous invocation we used to create a channel betweenibc-0
andibc-1
:hermes create channel --a-chain ibc-1 --b-chain ibc-2 --a-port transfer --b-port transfer --new-client-connection
(...) Success: Channel { ordering: Unordered, a_side: ChannelSide { chain: ProdChainHandle { chain_id: ChainId { id: "ibc-1", version: 1, }, runtime_sender: Sender { .. }, }, client_id: ClientId( "07-tendermint-1", ), connection_id: ConnectionId( "connection-1", ), port_id: PortId( "transfer", ), channel_id: ChannelId( "channel-1", ), }, b_side: ChannelSide { chain: ProdChainHandle { chain_id: ChainId { id: "ibc-2", version: 2, }, runtime_sender: Sender { .. }, }, client_id: ClientId( "07-tendermint-0", ), connection_id: ConnectionId( "connection-0", ), port_id: PortId( "transfer", ), channel_id: ChannelId( "channel-0", ), }, connection_delay: 0s, version: Some( "ics20-1", ), }
Note that the channel identifier on
ibc-1
ischannel-1
, and onibc-2
it ischannel-0
. -
Start Hermes using the
start
command:hermes start
Hermes will first relay the pending packets that have not been relayed and then start passive relaying by listening to and acting on packet events.
-
In a separate terminal, use the
ft-transfer
command to send:-
Two packets from
ibc-0
toibc-1
from source channelchannel-0
hermes tx ft-transfer --receiver-chain ibc-1 --sender-chain ibc-0 --sender-port transfer --sender-channel channel-0 --amount 9999 --timeout-height-offset 1000 --number-msgs 2
Success: [ SendPacket( SendPacket { height: revision: 0, height: 3056, packet: PortId("transfer") ChannelId("channel-0") Sequence(3), }, ), SendPacket( SendPacket { height: revision: 0, height: 3056, packet: PortId("transfer") ChannelId("channel-0") Sequence(4), }, ), ]
-
Two packets from
ibc-1
toibc-2
from source channelchannel-1
hermes tx ft-transfer --receiver-chain ibc-2 --sender-chain ibc-1 --sender-port transfer --sender-channel channel-1 --amount 9999 --timeout-height-offset 1000 --number-msgs 2
Success: [ SendPacket( SendPacket { height: revision: 1, height: 3076, packet: PortId("transfer") ChannelId("channel-1") Sequence(3), }, ), SendPacket( SendPacket { height: revision: 1, height: 3076, packet: PortId("transfer") ChannelId("channel-1") Sequence(4), }, ), ]
-
-
Observe the output on the relayer terminal, verify that the send events are processed, and that the
recv_packets
are sent out.(...) INFO ibc_relayer::link: [ibc-0 -> ibc-1] result events: UpdateClientEv(ev_h:1-3048, 07-tendermint-0(0-3057), ) WriteAcknowledgementEv(h:1-3048, seq:3, path:channel-0/transfer->channel-0/transfer, toh:1-4045, tos:0)) WriteAcknowledgementEv(h:1-3048, seq:4, path:channel-0/transfer->channel-0/transfer, toh:1-4045, tos:0)) INFO ibc_relayer::link: [ibc-0 -> ibc-1] success (...) INFO ibc_relayer::link: [ibc-1 -> ibc-0] clearing old packets INFO ibc_relayer::link: [ibc-1 -> ibc-0] received from query_txs [] INFO ibc_relayer::link: [ibc-1 -> ibc-0] finished clearing pending packets INFO ibc_relayer::link: [ibc-1 -> ibc-0] generate messages from batch with 2 events INFO ibc_relayer::link: [ibc-1 -> ibc-0] scheduling op. data with 2 msg(s) for Destination chain (height 1-3049) INFO ibc_relayer::link: [ibc-1 -> ibc-0] relay op. data to Destination, proofs height 1-3048, (delayed by: 2.154603ms) [try 1/10] INFO ibc_relayer::link: [ibc-1 -> ibc-0] prepending Destination client update @ height 1-3049 INFO ibc_relayer::link: [ibc-1 -> ibc-0] assembled batch of 3 message(s) INFO ibc_relayer::link: [ibc-1 -> ibc-0] result events: UpdateClientEv(ev_h:0-3059, 07-tendermint-0(1-3049), ) AcknowledgePacketEv(h:0-3059, seq:3, path:channel-0/transfer->channel-0/transfer, toh:1-4045, tos:0)) AcknowledgePacketEv(h:0-3059, seq:4, path:channel-0/transfer->channel-0/transfer, toh:1-4045, tos:0)) INFO ibc_relayer::link: [ibc-1 -> ibc-0] success (...)
-
Query the unreceived packets and acknowledgments on
ibc-1
andibc-2
from a different terminal:hermes query packet pending-sends --chain ibc-1 --port transfer --channel channel-0 hermes query packet pending-acks --chain ibc-0 --port transfer --channel channel-0 hermes query packet pending-sends --chain ibc-2 --port transfer --channel channel-0 hermes query packet pending-acks --chain ibc-1 --port transfer --channel channel-1
If everything went well, each of these commands should result in:
Success: []
Commands
The Commands
section presents the commands current available in Hermes
Sections
Commands to manage keys (private keys) for each chain.
Commands to manage configuration file, in particular to validate it.
Commands to manage clients, connections, channels.
Commands to start the relayer and relay packets.
Commands to listen for IBC events
Commands to perform client upgrade
Commands to monitor clients and submit evidence of misbehaviour
Commands to execute queries on configured chains
Commands to submit individual transactions to configured chains
Global options
Hermes accepts global options which affect all commands.
hermes v1.0.0-rc.2
Informal Systems <hello@informal.systems>
Implementation of `hermes`, an IBC Relayer developed in Rust.
FLAGS:
--config <CONFIG> Path to configuration file
--json Enable JSON output
Ordering of command-line options
The global options must be specified right after the hermes
command and before any subcommand.
The non-global options have to be specified after the subcommand.
hermes <GLOBAL_OPTIONS> subcommand <OPTIONS>
Example
To start
the relayer using the configuration file at /home/my_chain.toml
and enable JSON output:
hermes --config /home/my_chain.toml --json start
To query
all clients on a chain while enabling JSON output:
hermes --json query clients --host-chain ibc-1
JSON output
If the --json
option is supplied, all relayer commands will output single-line JSON values instead of plain text.
Log messages will be written to stderr
, while the final result will be written to stdout
, and everything
will be formatted as JSON.
This allows processing only the final output using jq
.
To process all the output using jq
, one can redirect stderr
to stdout
with hermes --json COMMAND 2>&1 | jq
.
Example
hermes --config /home/my_chain.toml --json create client --host-chain ibc-0 --reference-chain ibc-1
{"timestamp":"Apr 13 20:46:31.921","level":"INFO","fields":{"message":"Using default configuration from: '.hermes/config.toml'"},"target":"ibc_relayer_cli::commands"}
{"timestamp":"Apr 13 20:46:31.961","level":"INFO","fields":{"message":"running listener","chain.id":"ibc-1"},"target":"ibc_relayer::event::monitor"}
{"timestamp":"Apr 13 20:46:31.989","level":"INFO","fields":{"message":"running listener","chain.id":"ibc-0"},"target":"ibc_relayer::event::monitor"}
{"result":{"CreateClient":{"client_id":"07-tendermint-1","client_type":"Tendermint","consensus_height":{"revision_height":10060,"revision_number":1},"height":{"revision_height":10072,"revision_number":0}}},"status":"success"}
The first three lines are printed to stderr
, while the last line with a "result"
key is printed to stdout
.
Example
To improve the readability, pipe all of the output to jq
:
hermes --config /home/my_chain.toml --json create client --host-chain ibc-0 --reference-chain ibc-1 2>&1 | jq
{
"timestamp": "Apr 13 20:52:26.060",
"level": "INFO",
"fields": {
"message": "Using default configuration from: '.hermes/config.toml'"
},
"target": "ibc_relayer_cli::commands"
}
{
"timestamp": "Apr 13 20:52:26.082",
"level": "INFO",
"fields": {
"message": "running listener",
"chain.id": "ibc-1"
},
"target": "ibc_relayer::event::monitor"
}
{
"timestamp": "Apr 13 20:52:26.088",
"level": "INFO",
"fields": {
"message": "running listener",
"chain.id": "ibc-0"
},
"target": "ibc_relayer::event::monitor"
}
{
"result": {
"CreateClient": {
"client_id": "07-tendermint-5",
"client_type": "Tendermint",
"consensus_height": {
"revision_height": 10364,
"revision_number": 1
},
"height": {
"revision_height": 10375,
"revision_number": 0
}
}
},
"status": "success"
}
Example
To extract the identifer of the newly created client above:
hermes --config /home/my_chain.toml --json create client --host-chain ibc-0 --reference-chain ibc-1 | jq '.result.CreateClient.client_id'
"07-tendermint-2"
Adding Keys to the Relayer
WARNING: Currently the relayer does NOT support a
keyring
store to securely store the private key file. The key file will be stored on the local file system in the user $HOME folder under$HOME/.hermes/keys/
BREAKING: As of Hermes v1.0.0, the sub-command
keys restore
has been removed. Please use the sub-commandkeys add
in order to restore a key.
Using the keys
command you can add and list keys.
Show usage
To see the available sub-commands for the keys
command run:
hermes help keys
The available sub-commands are the following:
USAGE:
hermes keys <SUBCOMMAND>
DESCRIPTION:
Manage keys in the relayer for each chain
SUBCOMMANDS:
help Get usage information
add Adds key to a configured chain or restores a key to a configured chain
using a mnemonic
balance Query balance for a key from a configured chain. If no key is given, the
key is retrieved from the configuration file
delete Delete key(s) from a configured chain
list List keys configured on a chain
Key Seed file (Private Key)
In order to execute the command below you need a private key file (JSON). The relayer uses the private key file to sign the transactions submitted to the chain.
The private key file can be obtained by using the keys add
on a Cosmos chain. For example, the command for gaiad
is:
# The `key_name` parameter is the name of the key that will be found in the json output
# For example, in the "Two Local Chains" tutorial, we use "testkey".
gaiad keys add <key_name> --output json
The command outputs a JSON similar to the one below.
{
"name": "testkey",
"type": "local",
"address": "cosmos1tc3vcuxyyac0dmayf887t95tdg7qpyql48w7gj",
"pubkey": "cosmospub1addwnpepqgg7ng4ycm60pdxfzdfh4hjvkwcr3da59mr8k883vsstx60ruv7kur4525u",
"mnemonic": "[24 words mnemonic]"
}
You can save this to a file (e.g. key_seed.json
) and use it to add to the relayer with hermes keys add --chain <chain_id> --key-file key_seed.json
. See the Adding Keys
section for more details.
Adding and restoring Keys
The command keys add
has two exclusive flags, --key-file
and --mnemonic-file
which are respectively used to add and restore a key.
If a key with the same key_name
already exists, the flag --overwrite
must be passed in order to overwrite the existing key or else the command will abort.
hermes keys add [OPTIONS] --chain <CHAIN_ID> --key-file <KEY_FILE>
hermes keys add [OPTIONS] --chain <CHAIN_ID> --mnemonic-file <MNEMONIC_FILE>
DESCRIPTION:
Adds key to a configured chain or restores a key to a configured chain using a mnemonic
OPTIONS:
--hd-path <HD_PATH> Derivation path for this key [default: m/44'/118'/0'/0/0]
--key-name <KEY_NAME> Name of the key (defaults to the `key_name` defined in the config)
--overwrite Overwrite the key if there is already one with the same key name
FLAGS:
--chain <CHAIN_ID> Identifier of the chain
--key-file <KEY_FILE> Path to the key file
--mnemonic-file <MNEMONIC_FILE> Path to file containing mnemonic to restore the key from
Add a private key to a chain from a key file
hermes keys add [OPTIONS] --chain <CHAIN_ID> --key-file <KEY_FILE>
DESCRIPTION:
Adds key to a configured chain or restores a key to a configured chain using a mnemonic
OPTIONS:
--hd-path <HD_PATH> Derivation path for this key [default: m/44'/118'/0'/0/0]
--key-name <KEY_NAME> Name of the key (defaults to the `key_name` defined in the config)
--overwrite Overwrite the key if there is already one with the same key name
FLAGS:
--chain <CHAIN_ID> Identifier of the chain
--key-file <KEY_FILE> Path to the key file
To add a private key file to a chain:
hermes --config config.toml keys add --chain [CHAIN_ID] --key-file [PRIVATE_KEY_FILE]
The content of the file key should have the same format as the output of the gaiad keys add
command:
{
"name": "testkey",
"type": "local",
"address": "cosmos1tc3vcuxyyac0dmayf887t95tdg7qpyql48w7gj",
"pubkey": "cosmospub1addwnpepqgg7ng4ycm60pdxfzdfh4hjvkwcr3da59mr8k883vsstx60ruv7kur4525u",
"mnemonic": "[24 words mnemonic]"
}
If the command is successful a message similar to the one below will be displayed:
Success: Added key testkey ([ADDRESS]) on [CHAIN ID] chain
Key name: By default, the key will be named after the
key_name
property specified in the configuration file. To use a different key name, specify the--key-name
option when invokingkeys add
.hermes --config config.toml keys add --chain [CHAINID] --key-file [PRIVATE_KEY_FILE] --key-name [KEY_NAME]
Restore a private key to a chain from a mnemonic
hermes keys add [OPTIONS] --chain <CHAIN_ID> --mnemonic-file <MNEMONIC_FILE>
DESCRIPTION:
Adds key to a configured chain or restores a key to a configured chain using a mnemonic
OPTIONS:
--hd-path <HD_PATH> Derivation path for this key [default: m/44'/118'/0'/0/0]
--key-name <KEY_NAME> Name of the key (defaults to the `key_name` defined in the config)
--overwrite Overwrite the key if there is already one with the same key name
FLAGS:
--chain <CHAIN_ID> Identifier of the chain
--mnemonic-file <MNEMONIC_FILE> Path to file containing mnemonic to restore the key from
To restore a key from its mnemonic:
hermes --config config.toml keys add --chain [CHAIN_ID] --mnemonic-file "[MNEMONIC_FILE]"
or using an explicit derivation path, for example an Ethereum coin type (used for Evmos, Injective, Umee, Cronos, and possibly other networks):
hermes --config config.toml keys add --chain <CHAIN_ID> --mnemonic-file <MNEMONIC_FILE> --hd-path "m/44'/60'/0'/0/0"
The mnemonic file needs to have the 24 mnemonic words on the same line, separated by a white space. So the content should have the following format:
word1 word2 word3 ... word24
If the command is successful a message similar to the one below will be displayed:
Success: Restore key testkey ([ADDRESS]) on [CHAIN ID] chain
Key name: By default, the key will be named after the
key_name
property specified in the configuration file. To use a different key name, specify the--key-name
option when invokingkeys add
.hermes --config config.toml keys add --chain [CHAINID] --mnemonic-file "[MNEMONIC_FILE]" --key-name [KEY_NAME]
Delete keys
In order to delete the private keys added to chains use the keys delete
command
USAGE:
hermes keys delete [OPTIONS] --chain <CHAIN_ID>
DESCRIPTION:
hermes keys delete --chain <CHAIN_ID> --key-name <KEY_NAME>
hermes keys delete --chain <CHAIN_ID> --all
FLAGS:
--all Delete all keys
--chain <CHAIN_ID> Identifier of the chain
--key-name <KEY_NAME> Name of the key
Delete private keys that was previously added to a chain
To delete a single private key by name:
hermes --config config.toml keys delete --chain [CHAIN_ID] --key-name [KEY_NAME]
Alternatively, to delete all private keys added to a chain:
hermes --config config.toml keys delete --chain [CHAIN_ID] --all
List keys
In order to list the private keys added to chains use the keys list
command
USAGE:
hermes keys list --chain <CHAIN_ID>
DESCRIPTION:
List keys configured on a chain
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain
Listing the private key that was added to a chain
To list the private key file that was added to a chain:
hermes --config config.toml keys list --chain [CHAIN_ID]
If the command is successful a message similar to the one below will be displayed:
Success:
- user2 (cosmos1attn9fxrcvjz483w3tu4cfz77ldmlyujly3q3k)
- testkey (cosmos1dw88vdekeeuta5u50p6n5lt5v5c6y2we0pu8nz)
JSON:
hermes --json --config config.toml keys list --chain [CHAIN_ID] | jq
If the command is successful a message similar to the one below will be displayed:
{
"result": {
"testkey": {
"account": "cosmos1dw88vdekeeuta5u50p6n5lt5v5c6y2we0pu8nz",
"address": [ 107, 142, 118, 55, 54, 206, 120, 190, 211, 148, 120, 117, 58, 125, 116, 101, 49, 162, 41, 217 ],
"coin_type": 118,
"private_key": "(snip)",
"public_key": "xpub6Gc7ZUt2q1BiQYjhUextPv5bZLwosHigZYqEquPD6FkAGmHDrLiBgE5Xnh8XGZp79rAXtZn1Dt3DNQHxxgCgVQqfRMfVsRiXn6mwULBnYq7"
},
"user2": {
"account": "cosmos1attn9fxrcvjz483w3tu4cfz77ldmlyujly3q3k",
"address": [ 234, 215, 50, 164, 195, 195, 36, 42, 158, 46, 138, 249, 92, 36, 94, 247, 219, 191, 147, 146 ],
"coin_type": 118,
"private_key": "(snip)",
"public_key": "xpub6FmDbeGTWVjSvHrqHfrpnMTZxpPX1V7XFiq5nMuvgwX9jumt1yUuwNAUQo8Nn36unbFShg6iSjkfMBgeY49wik7rF91N2SHvarpX62ByWMf"
}
},
"status": "success"
}
Query balance
In order to retrieve the balance of an account associated with a key use the keys balance
command
USAGE:
hermes keys balance [OPTIONS] --chain <CHAIN_ID>
DESCRIPTION:
Query balance for a key from a configured chain. If no key is given, the key is retrieved from the configuration file
OPTIONS:
--key-name <KEY_NAME> (optional) name of the key (defaults to the `key_name` defined in
the config)
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain
If the command is successful a message with the following format will be displayed:
Success: balance for key `KEY_NAME`: 100000000000 stake
JSON:
hermes --json keys balance [OPTIONS] --chain <CHAIN_ID>
If the command is successful a message with the following format will be displayed:
{
"result": {
"amount": "99989207",
"denom": "stake"
},
"status": "success"
}
Config
Use the config validate
command to perform a quick syntactic validation of
your configuration file.
USAGE:
hermes config validate
DESCRIPTION:
validate the relayer configuration
Example
Validate the default config file, the path inferred automatically to be
$HOME/.hermes/config.toml
.
hermes config validate
hermes config validate
Jul 12 16:31:07.017 INFO using default configuration from '$HOME/.hermes/config.toml'
Success: "validation passed successfully"
Validate a config file at an arbitrary location:
hermes --config ./config.toml config validate
This one fails validation because we mistakenly added two separate sections for
the same chain ibc-1
:
hermes --config ./config.toml config validate
error: hermes fatal error: config error: config file has duplicate entry for the chain 'ibc-1'
Path Setup
This section describes a number of commands that can be used to manage clients, connections, channels.
CLI name | Description |
---|---|
create client | Create a client for source chain on destination chain |
update client | Update the specified client on destination chain |
create connection | Establish a connection using existing or new clients |
create channel | Establish a channel using a pre-existing connection, or alternatively create a new client and a new connection underlying the new channel |
Create
Use the create
commands to create new clients, connections, and channels.
USAGE:
hermes create <SUBCOMMAND>
DESCRIPTION:
Create objects (client, connection, or channel) on chains
SUBCOMMANDS:
channel Create a new channel between two chains using a pre-existing connection
client Create a new IBC client
connection Create a new connection between two chains
help Print this message or the help of the given subcommand(s)
Update
Use the update
commands to update a client.
USAGE:
hermes update <SUBCOMMAND>
DESCRIPTION:
Update objects (clients) on chains
SUBCOMMANDS:
help Get usage information
client Update an IBC client
Client
Table of Contents
Create Client
Use the create client
command to create a new client on a destination chain,
tracking the state of the source chain.
USAGE:
hermes create client [OPTIONS] --host-chain <HOST_CHAIN_ID> --reference-chain <REFERENCE_CHAIN_ID>
DESCRIPTION:
Create a new IBC client
OPTIONS:
--clock-drift <CLOCK_DRIFT>
The maximum allowed clock drift for this client.
The clock drift is a correction parameter. It helps deal with clocks that are only
approximately synchronized between the source and destination chains of this client. The
destination chain for this client uses the clock drift parameter when deciding to accept
or reject a new header (originating from the source chain) for this client. If this
option is not specified, a suitable clock drift value is derived from the chain
configurations.
--trust-threshold <TRUST_THRESHOLD>
Override the trust threshold specified in the configuration.
The trust threshold defines what fraction of the total voting power of a known and
trusted validator set is sufficient for a commit to be accepted going forward.
--trusting-period <TRUSTING_PERIOD>
Override the trusting period specified in the config.
The trusting period specifies how long a validator set is trusted for (must be shorter
than the chain's unbonding period).
REQUIRED:
--host-chain <HOST_CHAIN_ID>
Identifier of the chain that hosts the client
--reference-chain <REFERENCE_CHAIN_ID>
Identifier of the chain targeted by the client
Example
Create a new client on ibc-0
which tracks ibc-1
:
hermes create client --host-chain ibc-0 --reference-chain ibc-1
CreateClient(
Attributes {
height: Height {
revision: 0,
height: 286,
},
client_id: ClientId(
"07-tendermint-0",
),
client_type: Tendermint,
consensus_height: Height {
revision: 1,
height: 274,
},
},
),
)
A new client is created with identifier 07-tendermint-1
Update Client
Use the update client
command to update an existing client with a new consensus state.
Specific update and trusted heights can be specified.
USAGE:
hermes update client [OPTIONS] --host-chain <HOST_CHAIN_ID> --client <CLIENT_ID>
DESCRIPTION:
Update an IBC client
OPTIONS:
--height <REFERENCE_HEIGHT>
The target height of the client update. Leave unspecified for latest height.
--trusted-height <REFERENCE_TRUSTED_HEIGHT>
The trusted height of the client update. Leave unspecified for latest height.
REQUIRED:
--client <CLIENT_ID> Identifier of the chain targeted by the client
--host-chain <HOST_CHAIN_ID> Identifier of the chain that hosts the client
Update client with latest header
the client on ibc-0
with latest header of ibc-1
:
hermes update client --host-chain ibc-0 --client 07-tendermint-9
Success: UpdateClient(
UpdateClient {
common: Attributes {
height: Height { revision: 0, height: 303 },
client_id: ClientId(
"07-tendermint-1",
),
client_type: Tendermint,
consensus_height: Height { revision: 1, height: 293 },
},
header: Some(
Tendermint(
Header {...},
),
),
},
)
The client with identifier 07-tendermint-1
has been updated with the consensus state at height 1-293
.
Update a client to a specific target height
hermes update client --host-chain ibc-0 --client 07-tendermint-1 --height 320 --trusted-height 293
Success: UpdateClient(
UpdateClient {
common: Attributes {
height: Height { revision: 0, height: 555 },
client_id: ClientId(
"07-tendermint-1",
),
client_type: Tendermint,
consensus_height: Height { revision: 1, height: 320 },
},
header: Some(
Tendermint(
Header {...},
),
),
},
)
The client with identifier 07-tendermint-1
has been updated with the consensus state at height 1-320
, as specified.
Connection
Table of Contents
Establish Connection
Use the create connection
command to create a new connection.
USAGE:
hermes create connection [OPTIONS] --a-chain <A_CHAIN_ID> --b-chain <B_CHAIN_ID>
hermes create connection [OPTIONS] --a-chain <A_CHAIN_ID> --a-client <A_CLIENT_ID> --b-client <B_CLIENT_ID>
DESCRIPTION:
Create a new connection between two chains
OPTIONS:
--delay <DELAY> Delay period parameter for the new connection (seconds) [default: 0]
FLAGS:
--a-chain <A_CHAIN_ID> Identifier of the side `a` chain for the new connection
--a-client <A_CLIENT_ID> Identifier of client hosted on chain `a`; default: None (creates
a new client)
--b-chain <B_CHAIN_ID> Identifier of the side `b` chain for the new connection
--b-client <B_CLIENT_ID> Identifier of client hosted on chain `b`; default: None (creates
a new client)
Examples
New connection over new clients
Create a new connection between ibc-0
and ibc-1
over new clients:
hermes create connection --a-chain ibc-0 --b-chain ibc-1
🥂 ibc-0 => OpenInitConnection(
OpenInit(
Attributes {
height: Height { revision: 0, height: 4073 },
connection_id: Some(
ConnectionId(
"connection-8",
),
),
client_id: ClientId(
"07-tendermint-8",
),
counterparty_connection_id: None,
counterparty_client_id: ClientId(
"07-tendermint-8",
),
},
),
)
🥂 ibc-1 => OpenTryConnection(
OpenTry(
Attributes {
height: Height { revision: 1, height: 4069 },
connection_id: Some(
ConnectionId(
"connection-8",
),
),
client_id: ClientId(
"07-tendermint-8",
),
counterparty_connection_id: Some(
ConnectionId(
"connection-8",
),
),
counterparty_client_id: ClientId(
"07-tendermint-8",
),
},
),
)
🥂 ibc-0 => OpenAckConnection(
OpenAck(
Attributes {
height: Height { revision: 0, height: 4081 },
connection_id: Some(
ConnectionId(
"connection-8",
),
),
client_id: ClientId(
"07-tendermint-8",
),
counterparty_connection_id: Some(
ConnectionId(
"connection-8",
),
),
counterparty_client_id: ClientId(
"07-tendermint-8",
),
},
),
)
🥂 ibc-1 => OpenConfirmConnection(
OpenConfirm(
Attributes {
height: Height { revision: 1, height: 4073 },
connection_id: Some(
ConnectionId(
"connection-8",
),
),
client_id: ClientId(
"07-tendermint-8",
),
counterparty_connection_id: Some(
ConnectionId(
"connection-8",
),
),
counterparty_client_id: ClientId(
"07-tendermint-8",
),
},
),
)
🥂🥂🥂 Connection handshake finished for [Connection {
delay_period: 0s,
a_side: ConnectionSide {
chain: ProdChainHandle {
chain_id: ChainId {
id: "ibc-0",
version: 0,
},
runtime_sender: Sender { .. },
},
client_id: ClientId(
"07-tendermint-8",
),
connection_id: ConnectionId(
"connection-8",
),
},
b_side: ConnectionSide {
chain: ProdChainHandle {
chain_id: ChainId {
id: "ibc-1",
version: 1,
},
runtime_sender: Sender { .. },
},
client_id: ClientId(
"07-tendermint-8",
),
connection_id: ConnectionId(
"connection-8",
),
},
}]
Success: Connection {
delay_period: 0s,
a_side: ConnectionSide {
chain: ProdChainHandle {
chain_id: ChainId {
id: "ibc-0",
version: 0,
},
runtime_sender: Sender { .. },
},
client_id: ClientId(
"07-tendermint-8",
),
connection_id: ConnectionId(
"connection-8",
),
},
b_side: ConnectionSide {
chain: ProdChainHandle {
chain_id: ChainId {
id: "ibc-1",
version: 1,
},
runtime_sender: Sender { .. },
},
client_id: ClientId(
"07-tendermint-8",
),
connection_id: ConnectionId(
"connection-8",
),
},
}
New connection over existing clients
Create a new connection between ibc-0
and ibc-1
over existing clients,
both with client id 07-tendermint-0
:
hermes create connection --a-chain ibc-0 --a-client 07-tendermint-0 --b-client 07-tendermint-0
Notice that one can omit the destination chain parameter, as Hermes will automatically
figure it out by looking up the given client on ibc-0
.
Non-zero Delay Connection
A connection can be created with a delay period parameter. This parameter specifies a period of time that must elpase after a successful client state update and before a packet with proofs using its commitment root can pe processed on chain. For more information see how packet delay works and the connection delay specification.
Channel
Table of Contents
Establish Channel
Use the create channel
command to establish a new channel.
USAGE:
hermes create channel [OPTIONS] --a-chain <A_CHAIN_ID> --a-connection <A_CONNECTION_ID> --a-port <A_PORT_ID> --b-port <B_PORT_ID>
hermes create channel [OPTIONS] --a-chain <A_CHAIN_ID> --b-chain <B_CHAIN_ID> --a-port <A_PORT_ID> --b-port <B_PORT_ID> --new-client-connection
DESCRIPTION:
Create a new channel between two chains.
Can create a new channel using a pre-existing connection or alternatively, create a new client and a
new connection underlying the new channel if a pre-existing connection is not provided.
OPTIONS:
--channel-version <VERSION>
The version for the new channel
[aliases: chan-version]
--new-client-connection
Indicates that a new client and connection will be created underlying the new channel
[aliases: new-client-conn]
--order <ORDER>
The channel ordering, valid options 'unordered' (default) and 'ordered'
[default: ORDER_UNORDERED]
--yes
Skip new_client_connection confirmation
FLAGS:
--a-chain <A_CHAIN_ID>
Identifier of the side `a` chain for the new channel
--a-connection <A_CONNECTION_ID>
Identifier of the connection on chain `a` to use in creating the new channel
[aliases: a-conn]
--a-port <A_PORT_ID>
Identifier of the side `a` port for the new channel
--b-chain <B_CHAIN_ID>
Identifier of the side `b` chain for the new channel
--b-port <B_PORT_ID>
Identifier of the side `b` port for the new channel
Examples
New channel over an existing connection
This is the preferred way to create a new channel, by leveraging an existing connection.
Create a new unordered channel between ibc-0
and ibc-1
over an existing connection,
specifically the one we just created in the example above, with port name
transfer
on both sides:
hermes create channel --a-chain ibc-0 --a-connection connection-0 --a-port transfer --b-port transfer --order unordered
Notice that one can omit the destination chain parameter, as Hermes will automatically
figure it out by looking up the given connection on ibc-0
.
🥳 ibc-0 => OpenInitChannel(
OpenInit(
Attributes {
height: Height { revision: 0, height: 129 },
port_id: PortId("transfer"),
channel_id: Some(ChannelId("channel-1")),
connection_id: ConnectionId("connection-0"),
counterparty_port_id: PortId("transfer"),
counterparty_channel_id: None
}
)
)
🥳 ibc-1 => OpenTryChannel(
OpenTry(
Attributes {
height: Height { revision: 1, height: 126 },
port_id: PortId("transfer"),
channel_id: Some(ChannelId("channel-1")),
connection_id: ConnectionId("connection-0"),
counterparty_port_id: PortId("transfer"),
counterparty_channel_id: Some(ChannelId("channel-1"))
}
)
)
🥳 ibc-0 => OpenAckChannel(
OpenAck(
Attributes {
height: Height { revision: 0, height: 137 },
port_id: PortId("transfer"),
channel_id: Some(ChannelId("channel-1")),
connection_id: ConnectionId("connection-0"),
counterparty_port_id: PortId("transfer"),
counterparty_channel_id: Some(ChannelId("channel-1"))
}
)
)
🥳 ibc-1 => OpenConfirmChannel(
OpenConfirm(
Attributes {
height: Height { revision: 1, height: 129 },
port_id: PortId("transfer"),
channel_id: Some(ChannelId("channel-1")),
connection_id: ConnectionId("connection-0"),
counterparty_port_id: PortId("transfer"),
counterparty_channel_id: Some(ChannelId("channel-1"))
}
)
)
🥳 🥳 🥳 Channel handshake finished for Channel {
ordering: Unordered,
a_side: ChannelSide {
chain: ProdChainHandle {
chain_id: ChainId {
id: "ibc-0",
version: 0,
},
runtime_sender: Sender { .. },
},
client_id: ClientId(
"07-tendermint-0",
),
connection_id: ConnectionId(
"connection-0",
),
port_id: PortId(
"transfer",
),
channel_id: ChannelId(
"channel-1",
),
},
b_side: ChannelSide {
chain: ProdChainHandle {
chain_id: ChainId {
id: "ibc-1",
version: 1,
},
runtime_sender: Sender { .. },
},
client_id: ClientId(
"07-tendermint-0",
),
connection_id: ConnectionId(
"connection-0",
),
port_id: PortId(
"transfer",
),
channel_id: ChannelId(
"channel-1",
),
},
connection_delay: 0s,
}
Success: Channel {
ordering: Unordered,
a_side: ChannelSide {
chain: ProdChainHandle {
chain_id: ChainId {
id: "ibc-0",
version: 0,
},
runtime_sender: Sender { .. },
},
client_id: ClientId(
"07-tendermint-0",
),
connection_id: ConnectionId(
"connection-0",
),
port_id: PortId(
"transfer",
),
channel_id: ChannelId(
"channel-1",
),
},
b_side: ChannelSide {
chain: ProdChainHandle {
chain_id: ChainId {
id: "ibc-1",
version: 1,
},
runtime_sender: Sender { .. },
},
client_id: ClientId(
"07-tendermint-0",
),
connection_id: ConnectionId(
"connection-0",
),
port_id: PortId(
"transfer",
),
channel_id: ChannelId(
"channel-1",
),
},
connection_delay: 0s,
}
New channel over a new connection
Should you specifically want to create a new client and a new connection as part
of the create channel
flow, that option exists, though this is the
less-preferred option over the previous flow, as creating new clients and
connections should only be done in certain specific circumstances so as not to
create redundant resources.
Create a new unordered channel between ibc-0
and ibc-1
over a new
connection, using port name transfer
on both sides and accepting the
interactive prompt that pops up notifying you that a new client and a new
connection will be initialized as part of the process:
hermes create channel --a-chain ibc-0 --b-chain ibc-1 --a-port transfer --b-port transfer --order unordered --new-client-connection
🥂 ibc-0 => OpenInitConnection(
OpenInit(
Attributes {
height: Height { revision: 0, height: 66 },
connection_id: Some(
ConnectionId(
"connection-0",
),
),
client_id: ClientId(
"07-tendermint-0",
),
counterparty_connection_id: None,
counterparty_client_id: ClientId(
"07-tendermint-0",
),
},
),
)
🥂 ibc-1 => OpenTryConnection(
OpenTry(
Attributes {
height: Height { revision: 1, height: 64 },
connection_id: Some(
ConnectionId(
"connection-0",
),
),
client_id: ClientId(
"07-tendermint-0",
),
counterparty_connection_id: Some(
ConnectionId(
"connection-0",
),
),
counterparty_client_id: ClientId(
"07-tendermint-0",
),
},
),
)
🥂 ibc-0 => OpenAckConnection(
OpenAck(
Attributes {
height: Height { revision: 0, height: 76 },
connection_id: Some(
ConnectionId(
"connection-0",
),
),
client_id: ClientId(
"07-tendermint-0",
),
counterparty_connection_id: Some(
ConnectionId(
"connection-0",
),
),
counterparty_client_id: ClientId(
"07-tendermint-0",
),
},
),
)
🥂 ibc-1 => OpenConfirmConnection(
OpenConfirm(
Attributes {
height: Height { revision: 1, height: 68 },
connection_id: Some(
ConnectionId(
"connection-0",
),
),
client_id: ClientId(
"07-tendermint-0",
),
counterparty_connection_id: Some(
ConnectionId(
"connection-0",
),
),
counterparty_client_id: ClientId(
"07-tendermint-0",
),
},
),
)
🥂🥂🥂 Connection handshake finished for [Connection {
delay_period: 0s,
a_side: ConnectionSide {
chain: ProdChainHandle {
chain_id: ChainId {
id: "ibc-0",
version: 0,
},
runtime_sender: Sender { .. },
},
client_id: ClientId(
"07-tendermint-0",
),
connection_id: ConnectionId(
"connection-0",
),
},
b_side: ConnectionSide {
chain: ProdChainHandle {
chain_id: ChainId {
id: "ibc-1",
version: 1,
},
runtime_sender: Sender { .. },
},
client_id: ClientId(
"07-tendermint-0",
),
connection_id: ConnectionId(
"connection-0",
),
},
}]
🥳 ibc-0 => OpenInitChannel(
OpenInit(
Attributes {
height: Height { revision: 0, height: 78 },
port_id: PortId("transfer"),
channel_id: Some(ChannelId("channel-0")),
connection_id: ConnectionId("connection-0"),
counterparty_port_id: PortId("transfer"),
counterparty_channel_id: None
}
)
)
🥳 ibc-1 => OpenTryChannel(
OpenTry(
Attributes {
height: Height { revision: 1, height: 70 },
port_id: PortId("transfer"),
channel_id: Some(ChannelId("channel-0")),
connection_id: ConnectionId("connection-0"),
counterparty_port_id: PortId("transfer"),
counterparty_channel_id: Some(ChannelId("channel-0"))
}
)
)
🥳 ibc-0 => OpenAckChannel(
OpenAck(
Attributes {
height: Height { revision: 0, height: 81 },
port_id: PortId("transfer"),
channel_id: Some(ChannelId("channel-0")),
connection_id: ConnectionId("connection-0"),
counterparty_port_id: PortId("transfer"),
counterparty_channel_id: Some(ChannelId("channel-0"))
}
)
)
🥳 ibc-1 => OpenConfirmChannel
OpenConfirm
Attributes {
height: Height { revision: 1, height: 73 },
port_id: PortId("transfer"),
channel_id: Some(ChannelId("channel-0")),
connection_id: ConnectionId("connection-0"),
counterparty_port_id: PortId("transfer"),
counterparty_channel_id: Some(ChannelId("channel-0"))
}
)
)
🥳 🥳 🥳 Channel handshake finished for Channel {
ordering: Unordered,
a_side: ChannelSide {
chain: ProdChainHandle {
chain_id: ChainId {
id: "ibc-0",
version: 0,
},
runtime_sender: Sender { .. },
},
client_id: ClientId(
"07-tendermint-0",
),
connection_id: ConnectionId(
"connection-0",
),
port_id: PortId(
"transfer",
),
channel_id: ChannelId(
"channel-0",
),
},
b_side: ChannelSide {
chain: ProdChainHandle {
chain_id: ChainId {
id: "ibc-1",
version: 1,
},
runtime_sender: Sender { .. },
},
client_id: ClientId(
"07-tendermint-0",
),
connection_id: ConnectionId(
"connection-0",
),
port_id: PortId(
"transfer",
),
channel_id: ChannelId(
"channel-0",
),
},
connection_delay: 0s,
}
Success: Channel {
ordering: Unordered,
a_side: ChannelSide {
chain: ProdChainHandle {
chain_id: ChainId {
id: "ibc-0",
version: 0,
},
runtime_sender: Sender { .. },
},
client_id: ClientId(
"07-tendermint-0",
),
connection_id: ConnectionId(
"connection-0",
),
port_id: PortId(
"transfer",
),
channel_id: ChannelId(
"channel-0",
),
},
b_side: ChannelSide {
chain: ProdChainHandle {
chain_id: ChainId {
id: "ibc-1",
version: 1,
},
runtime_sender: Sender { .. },
},
client_id: ClientId(
"07-tendermint-0",
),
connection_id: ConnectionId(
"connection-0",
),
port_id: PortId(
"transfer",
),
channel_id: ChannelId(
"channel-0",
),
},
connection_delay: 0s,
}
A new channel with identifier channel-0
on both sides has been established on
a new connection with identifier connection-0
on both sides.
Relaying
This section describes the types of relaying that hermes can perform.
Hermes can send transactions triggered by IBC events. It currently handles channel handshake and packet events:
The start
Command
The start
command can be used to start hermes in IBC event listen mode.
USAGE:
hermes start [OPTIONS]
DESCRIPTION:
Start the relayer in multi-chain mode.
Relays packets and open handshake messages between all chains in the config.
OPTIONS:
--full-scan Force a full scan of the chains for clients, connections and channels
As described in next sub-sections, the type of relaying can be configured in the global
section of the configuration file, by specifying different values in strategy
field.
Packet Relaying
This section describes the configuration and commands that can be used to start the relayer and relay packets over one or multiple paths.
Table of Contents
The start
Command
To relay packets only configure the mode
section of the configuration file like so:
[global]
log_level = 'info'
[mode]
[mode.clients]
enabled = true
# ...
[mode.connections]
enabled = false
[mode.channels]
enabled = false
[mode.packets]
enabled = true
# ...
Then start hermes using the start command:
hermes start
The relayer sends packet transactions triggered by IBC packet events for all open channels between the configured chains. This is also referred to packet streaming.
Packet Streaming
After the relayer is started using the start
command, it listens to IBC packet events emitted by any of
the configured chains. Assuming the events are coming from a source
chain, the relayer builds packets
based on these events, packets that are then sent either to the source
chain or the counterparty (destination
) chain.
Current events and actions are:
send_packet
: the relayer builds a packet message with thepacket
obtained from the event and any required proofs obtained from the counterparty of the chain where the message is sent. The concrete packet is:MsgRecvPacket
, sent todestination
chain if the channel is in open state on thedestination
chain, and a timeout has not occurred,MsgTimeout
, sent to thesource
chain if the channel is in open state on thedestination
chain, but a timeout has occurred.MsgTimeoutOnClose
, sent to thesource
chain if the channel is in closed state on thedestination
chain.
write_acknowledgement
: the relayer builds aMsgAcknowledgement
packet that is sent to thedestination
chain.
In addition to these events, the relayer will also handle channel closing events:
chan_close_init
: the relayer builds aMsgChannelCloseConfirm
and sends it to thedestination
chain
Packet Delay
If the relay path is using a non-zero delay connection, then hermes
will delay all packet transactions. The delay is relative to the submission time for the client update at the height required by the packet proof.
The delay is used to prevent light client attacks and ensures that misbehavior detection finalizes before the transaction is submitted.
For more information on the misbehavior detector see the misbehaviour section.
Relaying of Handshake Messages
This section describes the configuration and commands that can be used to start the relayer and relay both handshake and packets for connections and channels.
The start
Command
To relay packets and handshake messages configure the mode
section of the configuration file like so:
[global]
log_level = 'info'
[mode]
[mode.clients]
enabled = true
# ...
[mode.connections]
enabled = true
[mode.channels]
enabled = true
[mode.packets]
enabled = true
# ...
Then start hermes using the start command:
hermes start
The relayer sends handshake and packet transactions triggered by IBC events.
Completing Channel Handshakes
After the relayer is started using the start
command, it scans the chain state and will resume the handshake for any
channels or connections that are not in open state. It then listens to IBC events emitted by any of
the configured chains.
Assuming the events are coming from a source
chain, the relayer determines the destination
chain and builds the handshake messages based on these events. These are then sent to the destination
chain.
In addition to the events described in Packet Relaying, the following IBC events may be handled:
-
Channels (if
mode.channels.enabled=true
):chan_open_init
: the relayer builds aMsgChannelOpenTry
messagechan_open_try
: the relayer builds aMsgChannelOpenAck
messagechan_open_ack
: the relayer builds aMsgChannelOpenConfirm
messagechan_open_confirm
: no message is sent out, channel opening is finished
-
Connections (if
mode.connections.enabled=true
):conn_open_init
: the relayer builds aMsgConnOpenTry
messageconn_open_try
: the relayer builds aMsgConnOpenAck
messageconn_open_ack
: the relayer builds aMsgConnOpenConfirm
messageconn_open_confirm
: no message is sent out, connection opening is finished
Clearing Packets
clear packets
This command clears outstanding packets on a given channel in both directions, by issuing the appropriate packet-recvs and packet-acks.
Usage
USAGE:
hermes clear packets [OPTIONS] --chain <CHAIN_ID> --port <PORT_ID> --channel <CHANNEL_ID>
DESCRIPTION:
Clear outstanding packets (i.e., packet-recv and packet-ack) on a given channel in both directions.
The channel is identified by the chain, port, and channel IDs at one of its ends
OPTIONS:
--counterparty-key-name <COUNTERPARTY_KEY_NAME>
use the given signing key for the counterparty chain (default: `counterparty_key_name`
config)
--key-name <KEY_NAME>
use the given signing key for the specified chain (default: `key_name` config)
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain
--channel <CHANNEL_ID> Identifier of the channel
--port <PORT_ID> Identifier of the port
Example
- Without Hermes running, send 3 packets over a channel, here
channel-13
:
❯ hermes tx ft-transfer --receiver-chain ibc1 --sender-chain ibc0 --sender-port transfer --sender-channel channel-13 --amount 9999 --timeout-height-offset 1000 --number-msgs 3
2022-02-24T14:16:28.295526Z INFO ThreadId(01) using default configuration from '/Users/coromac/.hermes/config.toml'
2022-02-24T14:16:28.330860Z INFO ThreadId(15) send_tx{id=ibc0}: refresh: retrieved account sequence=61 number=1
2022-02-24T14:16:28.350022Z INFO ThreadId(15) wait_for_block_commits: waiting for commit of tx hashes(s) AE4C3186778488E45670EB7303FA77E69B39F4E7C7494B05EC51E55136A373D6 id=ibc0
Success: [
SendPacket(
SendPacket {
height: Height {
revision: 0,
height: 86208,
},
packet: Packet {
sequence: Sequence(
14,
),
source_port: PortId(
"transfer",
),
source_channel: ChannelId(
"channel-13",
),
destination_port: PortId(
"transfer",
),
destination_channel: ChannelId(
"channel-12",
),
data: [ ... ],
timeout_height: Height {
revision: 0,
height: 87203,
},
timeout_timestamp: Timestamp {
time: None,
},
},
},
),
SendPacket(
SendPacket {
height: Height {
revision: 0,
height: 86208,
},
packet: Packet {
sequence: Sequence(
15,
),
source_port: PortId(
"transfer",
),
source_channel: ChannelId(
"channel-13",
),
destination_port: PortId(
"transfer",
),
destination_channel: ChannelId(
"channel-12",
),
data: [ ... ],
timeout_height: Height {
revision: 0,
height: 87203,
},
timeout_timestamp: Timestamp {
time: None,
},
},
},
),
SendPacket(
SendPacket {
height: Height {
revision: 0,
height: 86208,
},
packet: Packet {
sequence: Sequence(
16,
),
source_port: PortId(
"transfer",
),
source_channel: ChannelId(
"channel-13",
),
destination_port: PortId(
"transfer",
),
destination_channel: ChannelId(
"channel-12",
),
data: [ ... ],
timeout_height: Height {
revision: 0,
height: 87203,
},
timeout_timestamp: Timestamp {
time: None,
},
},
},
),
]
- Because the relayer is not running these packets won't be relayed,
as can be seen with the
query packet pending-sends
command:
❯ hermes query packet pending-sends --chain ibc1 --port transfer --channel channel-13
2022-02-24T14:21:28.874190Z INFO ThreadId(01) using default configuration from '/Users/coromac/.hermes/config.toml'
Success: [
14,
15,
16,
]
- We can clear them manually using the
clear packets
command:
❯ hermes clear packets --chain ibc0 --port transfer --channel channel-13
2022-02-24T14:17:25.748422Z INFO ThreadId(01) using default configuration from '/Users/coromac/.hermes/config.toml'
2022-02-24T14:17:25.799704Z INFO ThreadId(01) PacketRecvCmd{src_chain=ibc0 src_port=transfer src_channel=channel-13 dst_chain=ibc1}: found unprocessed SendPacket events for [Sequence(14), Sequence(15), Sequence(16)] (first 10 shown here; total=3)
2022-02-24T14:17:25.827177Z INFO ThreadId(01) PacketRecvCmd{src_chain=ibc0 src_port=transfer src_channel=channel-13 dst_chain=ibc1}: ready to fetch a scheduled op. data with batch of size 3 targeting Destination
2022-02-24T14:17:26.504798Z INFO ThreadId(01) PacketRecvCmd{src_chain=ibc0 src_port=transfer src_channel=channel-13 dst_chain=ibc1}:relay{odata=E96CV_cA5P ->Destination @0-86218; len=3}: assembled batch of 4 message(s)
2022-02-24T14:17:26.508873Z INFO ThreadId(29) send_tx{id=ibc1}: refresh: retrieved account sequence=54 number=1
2022-02-24T14:17:26.561715Z INFO ThreadId(29) wait_for_block_commits: waiting for commit of tx hashes(s) 07AA83524257105CC476063932A560893BE8F4E94C679BFD00F970FC248647E0 id=ibc1
2022-02-24T14:17:31.948950Z INFO ThreadId(01) PacketRecvCmd{src_chain=ibc0 src_port=transfer src_channel=channel-13 dst_chain=ibc1}:relay{odata=E96CV_cA5P ->Destination @0-86218; len=3}: [Sync->ibc1] result events:
UpdateClientEv(h: 0-86215, cs_h: 07-tendermint-3(0-86219))
WriteAcknowledgementEv(WriteAcknowledgement - h:0-86215, seq:14, path:channel-13/transfer->channel-12/transfer, toh:0-87203, tos:Timestamp(NoTimestamp)))
WriteAcknowledgementEv(WriteAcknowledgement - h:0-86215, seq:15, path:channel-13/transfer->channel-12/transfer, toh:0-87203, tos:Timestamp(NoTimestamp)))
WriteAcknowledgementEv(WriteAcknowledgement - h:0-86215, seq:16, path:channel-13/transfer->channel-12/transfer, toh:0-87203, tos:Timestamp(NoTimestamp)))
2022-02-24T14:17:31.949192Z INFO ThreadId(01) PacketRecvCmd{src_chain=ibc0 src_port=transfer src_channel=channel-13 dst_chain=ibc1}:relay{odata=E96CV_cA5P ->Destination @0-86218; len=3}: success
2022-02-24T14:17:31.989215Z INFO ThreadId(01) PacketAckCmd{src_chain=ibc1 src_port=transfer src_channel=channel-12 dst_chain=ibc0}: found unprocessed WriteAcknowledgement events for [Sequence(14), Sequence(15), Sequence(16)] (first 10 shown here; total=3)
2022-02-24T14:17:32.013500Z INFO ThreadId(01) PacketAckCmd{src_chain=ibc1 src_port=transfer src_channel=channel-12 dst_chain=ibc0}: ready to fetch a scheduled op. data with batch of size 3 targeting Destination
2022-02-24T14:17:33.211930Z INFO ThreadId(01) PacketAckCmd{src_chain=ibc1 src_port=transfer src_channel=channel-12 dst_chain=ibc0}:relay{odata=L4fnSXkxL_ ->Destination @0-86215; len=3}: assembled batch of 4 message(s)
2022-02-24T14:17:33.215803Z INFO ThreadId(15) send_tx{id=ibc0}: refresh: retrieved account sequence=62 number=1
2022-02-24T14:17:33.245229Z INFO ThreadId(15) wait_for_block_commits: waiting for commit of tx hashes(s) 62C69B1C46AF45182D5D99C6CB5EB301F8A402726772BA4EE067B18C68F2A4D6 id=ibc0
2022-02-24T14:17:37.465489Z INFO ThreadId(01) PacketAckCmd{src_chain=ibc1 src_port=transfer src_channel=channel-12 dst_chain=ibc0}:relay{odata=L4fnSXkxL_ ->Destination @0-86215; len=3}: [Sync->ibc0] result events:
UpdateClientEv(h: 0-86221, cs_h: 07-tendermint-3(0-86216))
AcknowledgePacketEv(h:0-86221, seq:14, path:channel-13/transfer->channel-12/transfer, toh:0-87203, tos:Timestamp(NoTimestamp)))
AcknowledgePacketEv(h:0-86221, seq:15, path:channel-13/transfer->channel-12/transfer, toh:0-87203, tos:Timestamp(NoTimestamp)))
AcknowledgePacketEv(h:0-86221, seq:16, path:channel-13/transfer->channel-12/transfer, toh:0-87203, tos:Timestamp(NoTimestamp)))
2022-02-24T14:17:37.465802Z INFO ThreadId(01) PacketAckCmd{src_chain=ibc1 src_port=transfer src_channel=channel-12 dst_chain=ibc0}:relay{odata=L4fnSXkxL_ ->Destination @0-86215; len=3}: success
Success: [
UpdateClient(
UpdateClient {
common: Attributes {
height: Height {
revision: 0,
height: 86215,
},
client_id: ClientId(
"07-tendermint-3",
),
client_type: Tendermint,
consensus_height: Height {
revision: 0,
height: 86219,
},
},
header: Some(
Tendermint(
Header {...},
),
),
},
),
WriteAcknowledgement(
WriteAcknowledgement {
height: Height {
revision: 0,
height: 86215,
},
packet: Packet {
sequence: Sequence(
14,
),
source_port: PortId(
"transfer",
),
source_channel: ChannelId(
"channel-13",
),
destination_port: PortId(
"transfer",
),
destination_channel: ChannelId(
"channel-12",
),
data: [ ... ],
timeout_height: Height {
revision: 0,
height: 87203,
},
timeout_timestamp: Timestamp {
time: None,
},
},
ack: [ ... ],
},
),
WriteAcknowledgement(
WriteAcknowledgement {
height: Height {
revision: 0,
height: 86215,
},
packet: Packet {
sequence: Sequence(
15,
),
source_port: PortId(
"transfer",
),
source_channel: ChannelId(
"channel-13",
),
destination_port: PortId(
"transfer",
),
destination_channel: ChannelId(
"channel-12",
),
data: [ ... ],
timeout_height: Height {
revision: 0,
height: 87203,
},
timeout_timestamp: Timestamp {
time: None,
},
},
ack: [ ... ],
},
),
WriteAcknowledgement(
WriteAcknowledgement {
height: Height {
revision: 0,
height: 86215,
},
packet: Packet {
sequence: Sequence(
16,
),
source_port: PortId(
"transfer",
),
source_channel: ChannelId(
"channel-13",
),
destination_port: PortId(
"transfer",
),
destination_channel: ChannelId(
"channel-12",
),
data: [ ... ],
timeout_height: Height {
revision: 0,
height: 87203,
},
timeout_timestamp: Timestamp {
time: None,
},
},
ack: [ ... ],
},
),
UpdateClient(
UpdateClient {
common: Attributes {
height: Height {
revision: 0,
height: 86221,
},
client_id: ClientId(
"07-tendermint-3",
),
client_type: Tendermint,
consensus_height: Height {
revision: 0,
height: 86216,
},
},
header: Some(
Tendermint(
Header {...},
),
),
},
),
AcknowledgePacket(
AcknowledgePacket {
height: Height {
revision: 0,
height: 86221,
},
packet: Packet {
sequence: Sequence(
14,
),
source_port: PortId(
"transfer",
),
source_channel: ChannelId(
"channel-13",
),
destination_port: PortId(
"transfer",
),
destination_channel: ChannelId(
"channel-12",
),
data: [],
timeout_height: Height {
revision: 0,
height: 87203,
},
timeout_timestamp: Timestamp {
time: None,
},
},
},
),
AcknowledgePacket(
AcknowledgePacket {
height: Height {
revision: 0,
height: 86221,
},
packet: Packet {
sequence: Sequence(
15,
),
source_port: PortId(
"transfer",
),
source_channel: ChannelId(
"channel-13",
),
destination_port: PortId(
"transfer",
),
destination_channel: ChannelId(
"channel-12",
),
data: [],
timeout_height: Height {
revision: 0,
height: 87203,
},
timeout_timestamp: Timestamp {
time: None,
},
},
},
),
AcknowledgePacket(
AcknowledgePacket {
height: Height {
revision: 0,
height: 86221,
},
packet: Packet {
sequence: Sequence(
16,
),
source_port: PortId(
"transfer",
),
source_channel: ChannelId(
"channel-13",
),
destination_port: PortId(
"transfer",
),
destination_channel: ChannelId(
"channel-12",
),
data: [],
timeout_height: Height {
revision: 0,
height: 87203,
},
timeout_timestamp: Timestamp {
time: None,
},
},
},
),
]
- The packets have now been successfully relayed:
❯ hermes query packet pending-sends --chain ibc1 --port transfer --channel channel-13
2022-02-24T14:21:28.874190Z INFO ThreadId(01) using default configuration from '/Users/coromac/.hermes/config.toml'
Success: []
Relayer Listen Mode
The relayer can be started in listen
mode to display the events emitted by a given chain. NewBlock
and Tx
IBC events are shown.
USAGE:
hermes listen [OPTIONS] --chain <CHAIN_ID>
DESCRIPTION:
Listen to and display IBC events emitted by a chain
OPTIONS:
--events <EVENT>... Add an event type to listen for, can be repeated. Listen for all
events by default (available: Tx, NewBlock)
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to listen for events from
Example
Start the relayer in listen mode for all ibc-0
events and observe the output:
hermes listen --chain ibc-0
EventBatch {
chain_id: ChainId {
id: "ibc-0",
version: 0,
},
height: block::Height(10914),
events: [
NewBlock(
NewBlock {
height: block::Height(10914),
},
),
],
}
EventBatch {
chain_id: ChainId {
id: "ibc-0",
version: 0,
},
height: block::Height(10915),
events: [
OpenInitConnection(
OpenInit(
Attributes {
height: block::Height(10915),
connection_id: Some(
ConnectionId(
"connection-3",
),
),
client_id: ClientId(
"07-tendermint-3",
),
counterparty_connection_id: None,
counterparty_client_id: ClientId(
"07-tendermint-5",
),
},
),
),
],
...
EventBatch {
chain_id: ChainId {
id: "ibc-0",
version: 0,
},
height: block::Height(10919),
events: [
UpdateClient(
UpdateClient(
Attributes {
height: block::Height(10919),
client_id: ClientId(
"07-tendermint-3",
),
client_type: Tendermint,
consensus_height: Height {
revision: 1,
height: 10907,
},
},
),
),
],
}
...
EventBatch {
chain_id: ChainId {
id: "ibc-0",
version: 0,
},
height: block::Height(10924),
events: [
UpdateClient(
UpdateClient(
Attributes {
height: block::Height(10924),
client_id: ClientId(
"07-tendermint-3",
),
client_type: Tendermint,
consensus_height: Height {
revision: 1,
height: 10912,
},
},
),
),
OpenAckConnection(
OpenAck(
Attributes {
height: block::Height(10924),
connection_id: Some(
ConnectionId(
"connection-3",
),
),
client_id: ClientId(
"07-tendermint-3",
),
counterparty_connection_id: Some(
ConnectionId(
"connection-5",
),
),
counterparty_client_id: ClientId(
"07-tendermint-5",
),
},
),
),
],
}
Filter events
The listen
command accepts an --event
flag to specify which event types to listen for.
At the moment, two event types are available:
NewBlock
Tx
The --event
flag can be repeated to specify more than one event type.
- To listen for only
NewBlock
events onibc-0
, invokehermes listen --chain ibc-0 --events NewBlock
- To listen for only
Tx
events onibc-0
, invokehermes listen --chain ibc-0 --events Tx
- To listen for both
NewBlock
andTx
events onibc-0
, invokehermes listen --chain ibc-0 --events NewBlock Tx
If the --event
flag is omitted, the relayer will subscribe to all event types.
Client Upgrade
Client Upgrade Command
Use the upgrade client
command to upgrade a client after a chain upgrade.
USAGE:
hermes upgrade client --host-chain <HOST_CHAIN_ID> --client <CLIENT_ID> --upgrade-height <REFERENCE_UPGRADE_HEIGHT>
DESCRIPTION:
Upgrade an IBC client
REQUIRED:
--client <CLIENT_ID>
Identifier of the client to be upgraded
--host-chain <HOST_CHAIN_ID>
Identifier of the chain that hosts the client
--upgrade-height <REFERENCE_UPGRADE_HEIGHT>
The height at which the reference chain halts for the client upgrade
Example
Here is an example of a chain upgrade proposal submission and client upgrade.
Testing Client Upgrade
Prerequisites
- gaiad
(v7.0.*)
, for example:
gaiad version --log_level error --long | head -n4
Testing procedure
Setup using Gaia manager
Note: The
gm.toml
file that we're using here looks like this:
[global]
add_to_hermes = true
auto_maintain_config = true
extra_wallets = 2
gaiad_binary = "$GOPATH/bin/gaiad"
hdpath = ""
home_dir = "$HOME/.gm"
ports_start_at = 27040
validator_mnemonic = ""
wallet_mnemonic = ""
[global.hermes]
binary = "<path/to/hermes>"
config = "$HOME/.hermes/config.toml"
log_level = "info"
telemetry_enabled = true
telemetry_host = "127.0.0.1"
telemetry_port = 3001
[ibc-0]
ports_start_at = 27000
[ibc-1]
ports_start_at = 27010
- Run the command
gm start
- Go to the file
$HOME/.gm/ibc-0/config/genesis.json
and changemax_deposit_period
andvoting_period
to a lower value, such as 120s - Run the commands:
gm reset
,gm hermes config
andgm hermes keys
Test upgrading chain and client
-
Create one client on
ibc-1
foribc-0
:hermes create client --host-chain ibc-1 --reference-chain ibc-0
Success: CreateClient( CreateClient( Attributes { height: Height { revision: 1, height: 9 }, client_id: ClientId( "07-tendermint-0", ), client_type: Tendermint, consensus_height: Height { revision: 0, height: 18 }, }, ), )
-
Create and submit an upgrade plan for chain
ibc-0
:Use the hermes test command to make an upgrade proposal. In the example below a software upgrade proposal is made for
ibc-0
, for the height300
blocks from latest height.10000000stake
is deposited. The proposal includes the upgraded client state constructed from the state of07-tendermint-0
client onibc-1
that was created in the previous step.hermes tx upgrade-chain --receiver-chain ibc-0 --sender-chain ibc-1 --sender-client 07-tendermint-0 --amount 10000000 --height-offset 60
Success: transaction::Hash(CE98D8D98091BA8016BD852D18056E54C4CB3C4525E7F40DD3C40B4FD0F2482B)
-
Verify that the proposal was accepted by querying the upgrade plan to check that it was submitted correctly.
Note: You can find the RPC port used to query the local node by running
gm ports
in order to see a list of the ports being used.gaiad --node tcp://localhost:27000 query gov proposal 1 --home $HOME/.gm/ibc-0/
If successful, you should see output like this. Note that the status of the proposal near the bottom of the output should be
PROPOSAL_STATUS_VOTING_PERIOD
indicating that the voting period is still ongoing.content: '@type': /ibc.core.client.v1.UpgradeProposal description: upgrade the chain software and unbonding period plan: height: "65" info: "" name: plan time: "0001-01-01T00:00:00Z" upgraded_client_state: null title: proposal 0 upgraded_client_state: '@type': /ibc.lightclients.tendermint.v1.ClientState allow_update_after_expiry: false allow_update_after_misbehaviour: false chain_id: ibc-0 frozen_height: revision_height: "0" revision_number: "0" latest_height: revision_height: "66" revision_number: "0" max_clock_drift: 0s proof_specs: - inner_spec: child_order: - 0 - 1 child_size: 33 empty_child: null hash: SHA256 max_prefix_length: 12 min_prefix_length: 4 leaf_spec: hash: SHA256 length: VAR_PROTO prefix: AA== prehash_key: NO_HASH prehash_value: SHA256 max_depth: 0 min_depth: 0 - inner_spec: child_order: - 0 - 1 child_size: 32 empty_child: null hash: SHA256 max_prefix_length: 1 min_prefix_length: 1 leaf_spec: hash: SHA256 length: VAR_PROTO prefix: AA== prehash_key: NO_HASH prehash_value: SHA256 max_depth: 0 min_depth: 0 trust_level: denominator: "0" numerator: "0" trusting_period: 0s unbonding_period: 1814400s upgrade_path: - upgrade - upgradedIBCState deposit_end_time: "2022-07-06T15:14:38.993051Z" final_tally_result: abstain: "0" "no": "0" no_with_veto: "0" "yes": "0" proposal_id: "1" status: PROPOSAL_STATUS_VOTING_PERIOD submit_time: "2022-07-06T15:12:38.993051Z" total_deposit: - amount: "10000000" denom: stake voting_end_time: "2022-07-06T15:14:38.993051Z" voting_start_time: "2022-07-06T15:12:38.993051Z"
-
Vote on the proposal
The parameter
1
should match theproposal_id:
from the upgrade proposal submitted at step 3. This command must be issued while the proposal status isPROPOSAL_STATUS_VOTING_PERIOD
. Confirm transaction when prompted.gaiad --node tcp://localhost:27000 tx gov vote 1 yes --home $HOME/.gm/ibc-0/data/ --keyring-backend test --keyring-dir $HOME/.gm/ibc-0/ --chain-id ibc-0 --from validator
confirm transaction before signing and broadcasting [y/N]: y txhash: 50CC1C39FBB14F99580A916ADE7F02883FFCC35D7862153F16BE86138151E17C
-
Test the
upgrade client
CLIThe following command waits for the reference chain
ibc-0
to halt and then performs the upgrade for client07-tendermint-0
onibc-1
. It outputs two events, one for the updated client state, and another for the upgraded state. The--upgrade-height 65
value is taken from theheight
in the upgrade plan output.hermes upgrade client --host-chain ibc-1 --client 07-tendermint-0 --upgrade-height 65
Success: [ UpdateClient( h: 1-68, cs_h: 07-tendermint-0(0-65), ), UpgradeClient( UpgradeClient( Attributes { height: Height { revision: 1, height: 68, }, client_id: ClientId( "07-tendermint-0", ), client_type: Tendermint, consensus_height: Height { revision: 0, height: 66, }, }, ), ), ]
Misbehaviour
Table of Contents
Monitoring Misbehaviour and Evidence Submission
Use the mishbehaviour
command to monitor the updates for a given client, detect certain types of misbehaviour and
submit evidence to the chain. If the evidence passes the on-chain validation, the client is frozen. Further packets
cannot be relayed using the frozen client.
USAGE:
hermes misbehaviour --chain <CHAIN_ID> --client <CLIENT_ID>
DESCRIPTION:
Listen to client update IBC events and handles misbehaviour
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain where client updates are monitored for
misbehaviour
--client <CLIENT_ID> Identifier of the client to be monitored for misbehaviour
The misbehaviour monitor starts by analyzing all headers used in prior client updates. Once finished it registers for update client events and checks any new headers for misbehaviour. If it detects evidence of misbehaviour, it submits a transaction with the evidence to the chain. If the chain validates the transaction then the monitor exits.
This is an experimental feature.
The following types of misbehaviour are handled:
-
Fork
Assumes at least one consensus state before the fork point exists. Let existing consensus states on chain B be:
[Sn,.., Sf, Sf-1, S0]
withSf-1
being the most recent state before the fork. Chain A is queried for a headerHf'
atSf.height
and if it is different than theHf
in the event for the client update (the one that has generatedSf
on chain), then the two headers are included in the evidence and submitted. Note that in this case the headers are different but have the same height. -
BFT time violation for an unavailable header
Some header with a height that is higher than the latest height on chain
A
has been accepted and a consensus state was created onB
. Note that this implies that the timestamp of this header must be within theclock_drift
of the client. Assume the client onB
has been updated withh2
(not present on/ produced by chainA
) and it has a timestamp oft2
that is at mostclock_drift
in the future. Then the latest header fromA
is fetched, let it beh1
, with a timestamp oft1
. Ift1 >= t2
then evidence of misbehavior is submitted to A.
Example
The hermes misbehaviour
outputs an error message displaying MISBEHAVIOUR DETECTED
:
hermes misbehaviour --chain ibc-0 --client 07-tendermint-0
Apr 13 20:04:03.347 INFO ibc_relayer::foreign_client: checking misbehaviour for consensus state heights [Height { revision: 1, height: 195 }, Height { revision: 1, height: 85 }, Height { revision: 1, height: 28 }]
Apr 13 20:04:04.425 ERROR ibc_relayer::foreign_client: MISBEHAVIOUR DETECTED ClientId("07-tendermint-0") h1: Height { revision: 1, height: 195 }-Height { revision: 1, height: 85 } h2: Height { revision: 1, height: 195 }-Height { revision: 1, height: 85 }, sending evidence
Apr 13 20:04:05.070 INFO ibc_relayer_cli::commands::misbehaviour: evidence submission result [ClientMisbehaviour(ClientMisbehaviour(Attributes { height: Height { revision: 0, height: 1521 }, client_id: ClientId("07-tendermint-0"), client_type: Tendermint, consensus_height: Height { revision: 1, height: 195 } }))]
Success: Some(
ClientMisbehaviour(
ClientMisbehaviour(
Attributes {
height: Height {
revision: 0,
height: 1521,
},
client_id: ClientId(
"07-tendermint-0",
),
client_type: Tendermint,
consensus_height: Height {
revision: 1,
height: 195,
},
},
),
),
)
Querying client state from this point will show the client is in frozen state, with frozen_height
indicating the height at which the client was frozen:
hermes query client state --chain ibc-0 --client 07-tendermint-0 | jq
{
"result": {
"allow_update_after_expiry": true,
"allow_update_after_misbehaviour": true,
"chain_id": "ibc-1",
"frozen_height": {
"revision_height": 16,
"revision_number": 1
},
"latest_height": {
"revision_height": 16,
"revision_number": 1
},
"max_clock_drift": {
"nanos": 0,
"secs": 3
},
"trust_level": {
"denominator": "3",
"numerator": "1"
},
"trusting_period": {
"nanos": 0,
"secs": 1209600
},
"unbonding_period": {
"nanos": 0,
"secs": 1814400
},
"upgrade_path": [
"upgrade",
"upgradedIBCState"
]
},
"status": "success"
}
Queries
Hermes supports querying for different objects that exist on a configured chain.
The query
command provides the following sub-commands:
CLI name | Description |
---|---|
client | Query information about clients |
clients | Query all clients |
connection | Query information about connections |
connections | Query the identifiers of all connections on a chain |
channel | Query information about channels |
channels | Query the identifiers of all channels on a given chain |
packet | Query information about packets |
transfer | Query information about token transfers |
tx | Query information about transactions |
Usage
USAGE:
hermes query <SUBCOMMAND>
DESCRIPTION:
Query objects from the chain
SUBCOMMANDS:
client Query information about clients
clients Query clients
connection Query information about connections
connections Query the identifiers of all connections on a chain
channel Query information about channels
channels Query the identifiers of all channels on a given chain
packet Query information about packets
transfer Query information about token transfers
tx Query information about transactions
Table of Contents
Query Clients
Use the query clients
command to query the identifiers of all clients on a given chain, called
the host chain.
USAGE:
hermes query clients [OPTIONS] --host-chain <HOST_CHAIN_ID>
DESCRIPTION:
Query the identifiers of all clients on a chain
OPTIONS:
--omit-chain-ids
Omit printing the reference (or target) chain for each client
--reference-chain <REFERENCE_CHAIN_ID>
Filter for clients which target a specific chain id (implies '--omit-chain-ids')
REQUIRED:
--host-chain <HOST_CHAIN_ID> Identifier of the chain to query
Example
Query all clients on ibc-1
:
hermes query clients --host-chain ibc-1
Success: [
ClientChain {
client_id: ClientId(
"07-tendermint-0",
),
chain_id: ChainId {
id: "ibc-0",
version: 0,
},
},
ClientChain {
client_id: ClientId(
"07-tendermint-1",
),
chain_id: ChainId {
id: "ibc-2",
version: 2,
},
},
]
Query all clients on ibc-1
having ibc-2
as their reference chain:
hermes query clients --host-chain ibc-1 --reference-chain ibc-2
Success: [
ClientId(
"07-tendermint-1",
),
]
Query Client Data
Use the query client
command to query the information about a specific client.
USAGE:
hermes query client <SUBCOMMAND>
DESCRIPTION:
Query information about clients
SUBCOMMANDS:
connections Query the client connections
consensus Query the client consensus state
header Query for the header used in a client update at a certain height
state Query the client full state
Query the client state
Use the query client state
command to query the client state of a client:
USAGE:
hermes query client state [OPTIONS] --chain <CHAIN_ID> --client <CLIENT_ID>
DESCRIPTION:
Query the client state
OPTIONS:
--height <HEIGHT> The chain height context for the query
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to query
--client <CLIENT_ID> Identifier of the client to query
Example
Query the state of client 07-tendermint-2
on ibc-1
:
hermes query client state --chain ibc-1 --client 07-tendermint-1
Success: ClientState {
chain_id: ChainId {
id: "ibc-2",
version: 2,
},
trust_level: TrustThresholdFraction {
numerator: 1,
denominator: 3,
},
trusting_period: 1209600s,
unbonding_period: 1814400s,
max_clock_drift: 3s,
frozen_height: Height {
revision: 0,
height: 0,
},
latest_height: Height {
revision: 2,
height: 3069,
},
upgrade_path: [
"upgrade",
"upgradedIBCState",
],
allow_update_after_expiry: true,
allow_update_after_misbehaviour: true,
}
Query the client consensus state
Use the query client consensus
command to query the consensus states of a given client, or the state at a specified height:
USAGE:
hermes query client consensus [OPTIONS] --chain <CHAIN_ID> --client <CLIENT_ID>
DESCRIPTION:
Query client consensus state
OPTIONS:
--consensus-height <CONSENSUS_HEIGHT>
Height of the client's consensus state to query
--height <HEIGHT>
The chain height context to be used, applicable only to a specific height
--heights-only
Show only consensus heights
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to query
--client <CLIENT_ID> Identifier of the client to query
Example
Query the states of client 07-tendermint-0
on ibc-0
:
hermes query client consensus --chain ibc-0 --client 07-tendermint-0 --heights-only
Success: [
Height {
revision: 1,
height: 3049,
},
Height {
revision: 1,
height: 2888,
},
Height {
revision: 1,
height: 2736,
},
Height {
revision: 1,
height: 2729,
},
Height {
revision: 1,
height: 2724,
},
Height {
revision: 1,
height: 2717,
},
]
Query ibc-0
at height 2800
for the consensus state for height 2724
:
hermes query client consensus --chain ibc-0 --client 07-tendermint-0 --consensus-height 2724 --height 2800
Success: ConsensusState {
timestamp: Time(
2021-04-13T14:11:20.969154Z
),
root: CommitmentRoot(
"371DD19003221B60162D42C78FD86ABF95A572F3D9497084584B75F97B05B70C"
),
next_validators_hash: Hash::Sha256(
740950668B6705A136D041914FC219045B1D0AD1C6A284C626BF5116005A98A7
),
}
Query the identifiers of all connections associated with a given client
Use the query client connections
command to query the connections associated with a given client:
USAGE:
hermes query client connections [OPTIONS] --chain <CHAIN_ID> --client <CLIENT_ID>
DESCRIPTION:
Query client connections
OPTIONS:
--height <HEIGHT> The chain height which this query should reflect
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to query
--client <CLIENT_ID> Identifier of the client to query
Example
Query the connections of client 07-tendermint-0
on ibc-0
:
hermes query client connections --chain ibc-0 --client 07-tendermint-0
Success: [
ConnectionId("connection-0"),
ConnectionId("connection-1"),
]
Query for the header used in a client update at a certain height
USAGE:
hermes query client header [OPTIONS] --chain <CHAIN_ID> --client <CLIENT_ID> --consensus-height <CONSENSUS_HEIGHT>
DESCRIPTION:
Query for the header used in a client update at a certain height
OPTIONS:
--height <HEIGHT> The chain height context for the query. Leave unspecified for latest
height.
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to query
--client <CLIENT_ID> Identifier of the client to query
--consensus-height <CONSENSUS_HEIGHT> Height of header to query
Example
Query for the header used in the 07-tendermint-0
client update at height 2724 on ibc-0
:
hermes query client header --chain ibc-0 --client 07-tendermint-0 --consensus-height 2724
Success: [
UpdateClient(
UpdateClient {
common: Attributes {
height: Height {
revision: 0,
height: 0,
},
client_id: ClientId(
"07-tendermint-0",
),
client_type: Tendermint,
consensus_height: Height {
revision: 1,
height: 2724,
},
},
header: Some(
Tendermint(...),
),
},
),
]
Table of Contents
Query Connections
Use the query connections
command to query the identifiers of all connections on a given chain.
USAGE:
hermes query connections --chain <CHAIN_ID>
DESCRIPTION:
Query the identifiers of all connections on a chain
OPTIONS:
--counterparty-chain <COUNTERPARTY_CHAIN_ID>
Filter the query response by the counterparty chain
--verbose
Enable verbose output, displaying the client for each connection in the response
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to query
Example
Query all connections on ibc-1
:
hermes query connections --chain ibc-1
Success: [
ConnectionId(
"connection-0",
),
ConnectionId(
"connection-1",
),
]
Query Connection Data
Use the query connection
commands to query a specific connection.
USAGE:
hermes query connection <SUBCOMMAND>
DESCRIPTION:
Query information about connection(s)
SUBCOMMANDS:
end query connection end
channels query connection channels
Query the connection end data
Use the query connection end
command to query the connection end:
USAGE:
hermes query connection end [OPTIONS] --chain <CHAIN_ID> --connection <CONNECTION_ID>
DESCRIPTION:
Query connection end
OPTIONS:
--height <HEIGHT> Height of the state to query. Leave unspecified for latest height.
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to query
--connection <CONNECTION_ID> Identifier of the connection to query [aliases: conn]
Example
Query the connection end of connection connection-1
on ibc-1
:
hermes query connection end --chain ibc-1 --connection connection-1
Success: ConnectionEnd {
state: Open,
client_id: ClientId(
"07-tendermint-1",
),
counterparty: Counterparty {
client_id: ClientId(
"07-tendermint-0",
),
connection_id: Some(
ConnectionId(
"connection-0",
),
),
prefix: ibc,
},
versions: [
Version {
identifier: "1",
features: [
"ORDER_ORDERED",
"ORDER_UNORDERED",
],
},
],
delay_period: 0s,
}
Query the identifiers of all channels associated with a given connection
Use the query connection channels
command to query the identifiers of the channels associated with a given connection:
USAGE:
hermes query connection channels --chain <CHAIN_ID> --connection <CONNECTION_ID>
DESCRIPTION:
Query connection channels
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to query
--connection <CONNECTION_ID> Identifier of the connection to query [aliases: conn]
Example
Query the channels associated with connection connection-1
on ibc-1
:
hermes query connection channels --chain ibc-1 --connection connection-1
Success: [
PortChannelId {
channel_id: ChannelId(
"channel-0",
),
port_id: PortId(
"transfer",
),
},
PortChannelId {
channel_id: ChannelId(
"channel-1",
),
port_id: PortId(
"transfer",
),
},
]
Table of Contents
Query Channels
Use the query channels
command to query the identifiers of all channels on a given chain.
USAGE:
hermes query channels [OPTIONS] --chain <CHAIN_ID>
DESCRIPTION:
Query the identifiers of all channels on a given chain
OPTIONS:
--counterparty-chain <COUNTERPARTY_CHAIN_ID>
Filter the query response by the this counterparty chain
--verbose
Enable verbose output, displaying the client and connection ids for each channel in the
response
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to query
Example
Query all channels on ibc-1
:
hermes query channels --chain ibc-1
Success: [
PortChannelId {
channel_id: ChannelId(
"channel-0",
),
port_id: PortId(
"transfer",
),
},
PortChannelId {
channel_id: ChannelId(
"channel-1",
),
port_id: PortId(
"transfer",
),
},
]
Query Channel Data
Use the query channel
commands to query the information about a specific channel.
USAGE:
hermes query channel <SUBCOMMAND>
DESCRIPTION:
Query information about channels
SUBCOMMANDS:
client Query channel's client state
end Query channel end
ends Query channel ends and underlying connection and client objects
Query the channel end data
Use the query channel end
command to query the channel end:
USAGE:
hermes query channel end [OPTIONS] --chain <CHAIN_ID> --port <PORT_ID> --channel <CHANNEL_ID>
DESCRIPTION:
Query channel end
OPTIONS:
--height <HEIGHT> Height of the state to query
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to query
--channel <CHANNEL_ID> Identifier of the channel to query [aliases: chan]
--port <PORT_ID> Identifier of the port to query
Example
Query the channel end of channel channel-1
on port transfer
on ibc-1
:
hermes query channel end --chain ibc-1 --port transfer --channel channel-1
Success: ChannelEnd {
state: Open,
ordering: Unordered,
remote: Counterparty {
port_id: PortId(
"transfer",
),
channel_id: Some(
ChannelId(
"channel-0",
),
),
},
connection_hops: [
ConnectionId(
"connection-1",
),
],
version: "ics20-1",
}
Query the channel data for both ends of a channel
Use the query channel ends
command to obtain both ends of a channel:
USAGE:
hermes query channel ends [OPTIONS] --chain <CHAIN_ID> --port <PORT_ID> --channel <CHANNEL_ID>
DESCRIPTION:
Query channel ends and underlying connection and client objects
OPTIONS:
--height <HEIGHT> Height of the state to query
--verbose Enable verbose output, displaying all details of channels, connections
& clients
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to query
--channel <CHANNEL_ID> Identifier of the channel to query [aliases: chan]
--port <PORT_ID> Identifier of the port to query
Example
Query the channel end of channel channel-1
on port transfer
on ibc-0
:
hermes query channel ends --chain ibc-0 --port transfer --channel channel-1
Success: ChannelEndsSummary {
chain_id: ChainId {
id: "ibc-0",
version: 0,
},
client_id: ClientId(
"07-tendermint-1",
),
connection_id: ConnectionId(
"connection-1",
),
channel_id: ChannelId(
"channel-1",
),
port_id: PortId(
"transfer",
),
counterparty_chain_id: ChainId {
id: "ibc-2",
version: 2,
},
counterparty_client_id: ClientId(
"07-tendermint-1",
),
counterparty_connection_id: ConnectionId(
"connection-1",
),
counterparty_channel_id: ChannelId(
"channel-1",
),
counterparty_port_id: PortId(
"transfer",
),
}
Passing the --verbose
flag will additionally print all the details of the
channel, connection, and client on both ends.
Query the channel client state
Use the query channel client
command to obtain the channel's client state:
USAGE:
hermes query channel client --chain <CHAIN_ID> --port <PORT_ID> --channel <CHANNEL_ID>
DESCRIPTION:
Query channel's client state
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to query
--channel <CHANNEL_ID> Identifier of the channel to query [aliases: chan]
--port <PORT_ID> Identifier of the port to query
If the command is successful a message with the following format will be displayed:
Success: Some(
IdentifiedAnyClientState {
client_id: ClientId(
"07-tendermint-0",
),
client_state: Tendermint(
ClientState {
chain_id: ChainId {
id: "network2",
version: 0,
},
trust_level: TrustThreshold {
numerator: 1,
denominator: 3,
},
trusting_period: 1209600s,
unbonding_period: 1814400s,
max_clock_drift: 40s,
latest_height: Height {
revision: 0,
height: 2775,
},
proof_specs: ProofSpecs(
[
ProofSpec(
ProofSpec {
leaf_spec: Some(
LeafOp {
hash: Sha256,
prehash_key: NoHash,
prehash_value: Sha256,
length: VarProto,
prefix: [
0,
],
},
),
inner_spec: Some(
InnerSpec {
child_order: [
0,
1,
],
child_size: 33,
min_prefix_length: 4,
max_prefix_length: 12,
empty_child: [],
hash: Sha256,
},
),
max_depth: 0,
min_depth: 0,
},
),
ProofSpec(
ProofSpec {
leaf_spec: Some(
LeafOp {
hash: Sha256,
prehash_key: NoHash,
prehash_value: Sha256,
length: VarProto,
prefix: [
0,
],
},
),
inner_spec: Some(
InnerSpec {
child_order: [
0,
1,
],
child_size: 32,
min_prefix_length: 1,
max_prefix_length: 1,
empty_child: [],
hash: Sha256,
},
),
max_depth: 0,
min_depth: 0,
},
),
],
),
upgrade_path: [
"upgrade",
"upgradedIBCState",
],
allow_update: AllowUpdate {
after_expiry: true,
after_misbehaviour: true,
},
frozen_height: None,
},
),
},
)
JSON:
hermes --json query channel client --chain <CHAIN_ID> --port <PORT_ID> --channel <CHANNEL_ID>
If the command is successful a message with the following format will be displayed:
{
"result":
{
"client_id":"07-tendermint-0",
"client_state":
{
"allow_update":
{
"after_expiry":true,
"after_misbehaviour":true
},
"chain_id":"network2",
"frozen_height":null,
"latest_height":
{
"revision_height":2775,
"revision_number":0
},
"max_clock_drift":
{
"nanos":0,
"secs":40
},
"proof_specs":
[
{
"inner_spec":
{
"child_order":[0,1],
"child_size":33,
"empty_child":"",
"hash":1,
"max_prefix_length":12,
"min_prefix_length":4
},
"leaf_spec":
{
"hash":1,
"length":1,
"prefix":"AA==",
"prehash_key":0,
"prehash_value":1
},
"max_depth":0,
"min_depth":0
},
{
"inner_spec":
{
"child_order":[0,1],
"child_size":32,
"empty_child":"",
"hash":1,
"max_prefix_length":1,
"min_prefix_length":1
},
"leaf_spec":
{
"hash":1,
"length":1,
"prefix":"AA==",
"prehash_key":0,
"prehash_value":1
},
"max_depth":0,
"min_depth":0
}
],
"trust_level":
{
"denominator":3,
"numerator":1
},
"trusting_period":
{
"nanos":0,
"secs":1209600
},
"type":"Tendermint",
"unbonding_period":
{
"nanos":0,
"secs":1814400
},
"upgrade_path":["upgrade","upgradedIBCState"]
},
"type":"IdentifiedAnyClientState"
},
"status":"success"
}
Packet Queries
Use the query packet
commands to query information about packets.
USAGE:
hermes query packet <SUBCOMMAND>
OPTIONS:
-h, --help Print help information
SUBCOMMANDS:
ack Query packet acknowledgment
acks Query packet acknowledgments
commitment Query packet commitment
commitments Query packet commitments
pending Output a summary of pending packets in both directions
pending-acks Query pending acknowledgments
pending-sends Query pending packets
help Print this message or the help of the given subcommand(s)
Table of Contents
- Pending Packets
- Packet Commitments
- Packet Commitment with Sequence
- Packet Acknowledgments
- Packet Acknowledgment with Sequence
- Unreceived Packets
- Unreceived Acknowledgments
Pending Packets
Use the query packet pending
command to query the sequence numbers of all packets that have not yet been received or acknowledged, at both ends of a channel.
USAGE:
hermes query packet pending --chain <CHAIN_ID> --port <PORT_ID> --channel <CHANNEL_ID>
DESCRIPTION:
Output a summary of pending packets in both directions
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain at one end of the channel
--channel <CHANNEL_ID> Channel identifier on the chain given by <CHAIN_ID> [aliases:
chan]
--port <PORT_ID> Port identifier on the chain given by <CHAIN_ID>
Example
Query the sequence numbers of all packets that either not yet been received or not yet been acknowledged, at both ends of the channel channel-1
.
$ hermes query packet pending --chain ibc-0 --port transfer --channel channel-1
Success: Summary {
forward: PendingPackets {
unreceived_packets: [
2203,
...
2212,
],
unreceived_acks: [
2183,
...
2202,
],
},
reverse: PendingPackets {
unreceived_packets: [
14,
...
23,
],
unreceived_acks: [
4,
...
13,
],
},
}
Packet Commitments
Use the query packet commitments
command to query the sequence numbers of all packets that have been sent but not yet acknowledged (these are the packets that still have their commitments stored).
USAGE:
hermes query packet commitments --chain <CHAIN_ID> --port <PORT_ID> --channel <CHANNEL_ID>
DESCRIPTION:
Query packet commitments
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to query
--channel <CHANNEL_ID> Identifier of the channel to query [aliases: chan]
--port <PORT_ID> Identifier of the port to query
Example
Query ibc-0
for the sequence numbers of packets that still have commitments on ibc-0
and that were sent on transfer
port and channel-0
:
hermes query packet commitments --chain ibc-0 --port transfer --channel channel-0
Success: PacketSeqs {
height: Height {
revision: 0,
height: 9154,
},
seqs: [
1,
2,
3
],
}
Packet Commitment with Sequence
Use the query packet commitment
command to query the commitment value of a packet with a given sequence number.
USAGE:
hermes query packet commitment [OPTIONS] --chain <CHAIN_ID> --port <PORT_ID> --channel <CHANNEL_ID> --sequence <SEQUENCE>
DESCRIPTION:
Query packet commitment
OPTIONS:
--height <HEIGHT> Height of the state to query. Leave unspecified for latest height.
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to query
--channel <CHANNEL_ID> Identifier of the channel to query [aliases: chan]
--port <PORT_ID> Identifier of the port to query
--sequence <SEQUENCE> Sequence of packet to query [aliases: seq]
Example
Query ibc-0
for the commitment of packet with sequence 3
sent on transfer
port and channel-0
:
hermes query packet commitment --chain ibc-0 --port transfer --channel channel-0 --sequence 3
Success: "F9458DC7EBEBCD6D18E983FCAB5BD752CC2A74532BBD50B812DB229997739EFC"
Packet Acknowledgments
Use the query packet acknowledgments
command to query the sequence numbers of all packets that have been acknowledged.
USAGE:
hermes query packet acks --chain <CHAIN_ID> --port <PORT_ID> --channel <CHANNEL_ID>
DESCRIPTION:
Query packet acknowledgments
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to query
--channel <CHANNEL_ID> Identifier of the channel to query [aliases: chan]
--port <PORT_ID> Identifier of the port to query
Example
Query ibc-1
for the sequence numbers of packets acknowledged that were received on transfer
port and channel-1
:
hermes query packet acks --chain ibc-1 --port transfer --channel channel-1
Success: PacketSeqs {
height: Height {
revision: 1,
height: 9547,
},
seqs: [
1,
2,
3
],
}
Packet Acknowledgment with Sequence
Use the query packet acknowledgment
command to query the acknowledgment value of a packet with a given sequence number.
USAGE:
hermes query packet ack [OPTIONS] --chain <CHAIN_ID> --port <PORT_ID> --channel <CHANNEL_ID> --sequence <SEQUENCE>
DESCRIPTION:
Query packet acknowledgment
OPTIONS:
--height <HEIGHT> Height of the state to query. Leave unspecified for latest height.
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to query
--channel <CHANNEL_ID> Identifier of the channel to query [aliases: chan]
--port <PORT_ID> Identifier of the port to query
--sequence <SEQUENCE> Sequence of packet to query [aliases: seq]
Example
Query ibc-1
for the acknowledgment of packet with sequence 2
received on transfer
port and channel-1
:
hermes query packet ack --chain ibc-1 --port transfer --channel channel-1 --sequence 2
Success: "08F7557ED51826FE18D84512BF24EC75001EDBAF2123A477DF72A0A9F3640A7C"
Unreceived Packets
Use the query packet pending-sends
command to query the sequence numbers of all packets that have been sent on the source chain but not yet received on the destination chain.
USAGE:
hermes query packet pending-sends --chain <CHAIN_ID> --port <PORT_ID> --channel <CHANNEL_ID>
DESCRIPTION:
Query pending packets
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain for the pending sequences
--channel <CHANNEL_ID> Channel identifier [aliases: chan]
--port <PORT_ID> Port identifier
Example
Query transfer
port and channel-1
on ibc-1
for the sequence numbers of packets sent on ibc-0
but not yet received:
hermes query packet pending-sends --chain ibc-1 --port transfer --channel channel-1
Success: [
1,
2,
3
]
Unreceived Acknowledgments
Use the query packet pending-acks
command to query the sequence numbers of all packets that have not yet been acknowledged.
USAGE:
hermes query packet pending-acks --chain <CHAIN_ID> --port <PORT_ID> --channel <CHANNEL_ID>
DESCRIPTION:
Query pending acknowledgments
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to query the pending acknowledgments
--channel <CHANNEL_ID> Channel identifier [aliases: chan]
--port <PORT_ID> Port identifier
Example
Query transfer
port and channel-0
on ibc-0
for the sequence numbers of packets received by ibc-1
but not yet acknowledged on ibc-0
:
hermes query packet pending-acks --chain ibc-0 --port transfer --channel channel-0
Success: [
1,
2,
3
]
Tx Queries
Use the query tx
command to query information about transaction(s).
USAGE:
hermes query tx <SUBCOMMAND>
DESCRIPTION:
Query information about transactions
SUBCOMMANDS:
events Query the events emitted by transaction
Table of Contents
Transaction Events
Use the query tx events
command to obtain a list of events that a chain generated as a consequence of
delivering a transaction.
USAGE:
hermes query tx events --chain <CHAIN_ID> --hash <HASH>
DESCRIPTION:
Query the events emitted by transaction
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain to query
--hash <HASH> Transaction hash to query
Example
Query chain ibc-0
for the events emitted due to transaction with hash
6EDBBCBCB779F9FC9D6884ACDC4350E69720C4B362E4ACE6C576DE792F837490
:
hermes query tx events --chain ibc-0 --hash 6EDBBCBCB779F9FC9D6884ACDC4350E69720C4B362E4ACE6C576DE792F837490
Success: [
SendPacket(
SendPacket {
height: Height {
revision: 4,
height: 6628239,
},
packet: PortId("transfer") ChannelId("channel-139") Sequence(2),
},
),
]
Transfer Queries
Use the query transfer
command to query information about transfer(s).
USAGE:
hermes query transfer <SUBCOMMAND>
DESCRIPTION:
Query information about token transfers
SUBCOMMANDS:
denom-trace Query the denomination trace info from a trace hash
Table of Contents
Denomination Trace
Use the query transfer denom-trace
command to obtain the path and base denomination of a given trace hash.
USAGE:
hermes query transfer denom-trace --chain <CHAIN_ID> --hash <HASH>
DESCRIPTION:
Query the denomination trace info from a trace hash
REQUIRED:
--chain <CHAIN_ID> Identifier of the chain
--hash <HASH> Trace hash to query
Example
Query chain ibc-1
for the path and base denomination of the trace hash 27A6394C3F9FF9C9DCF5DFFADF9BB5FE9A37C7E92B006199894CF1824DF9AC7C
:
hermes query transfer denom-trace --chain ibc-1 --hash 27A6394C3F9FF9C9DCF5DFFADF9BB5FE9A37C7E92B006199894CF1824DF9AC7C
Success: base_denom: samoleans
path: transfer/channel-0
Or with a JSON output:
hermes query transfer denom-trace --chain ibc-1 --hash 27A6394C3F9FF9C9DCF5DFFADF9BB5FE9A37C7E92B006199894CF1824DF9AC7C
{
"result":{
"base_denom":"samoleans",
"path":"transfer/channel-0"
},
"status":"success"
}
Transactions
There are a number of simple commands that perform minimal validation, build and send IBC transactions.
The tx
command provides the following sub-commands:
CLI name | Description |
---|---|
conn-init | Initialize a connection (ConnectionOpenInit) |
conn-try | Relay the connection attempt (ConnectionOpenTry) |
conn-ack | Relay acknowledgment of a connection attempt (ConnectionOpenAck) |
conn-confirm | Confirm opening of a connection (ConnectionOpenConfirm) |
chan-open-init | Initialize a channel (ChannelOpenInit) |
chan-open-try | Relay the channel attempt (ChannelOpenTry) |
chan-open-ack | Relay acknowledgment of a channel attempt (ChannelOpenAck) |
chan-open-confirm | Confirm opening of a channel (ChannelOpenConfirm) |
chan-close-init | Initiate the closing of a channel (ChannelCloseInit) |
chan-close-confirm | Confirm the closing of a channel (ChannelCloseConfirm) |
ft-transfer | Send a fungible token transfer test transaction (ICS20 MsgTransfer |
packet-recv | Relay receive or timeout packets |
packet-ack | Relay acknowledgment packets |
upgrade-chain | Send an IBC upgrade plan |
The main purpose of these commands is to support development and testing, and continuous integration. These CLIs take quite a few parameters and they are explained in the individual sub-sections.
At a high level, most commands follow this template:
hermes tx <ibc-datagram> <dst-chain-id> <src-chain-id> [-d <dst-obj-id> -s <src-obj-id>]*
In the command template above:
-
ibc-datagram
- identifies the "main" IBC message that is being sent, e.g.conn-init
,conn-try
,chan-open-init
, etc. To ensure successful processing on the receiving chain, the majority of these commands build and send two messages: oneUpdateClient
message followed by the actual IBC message. These two messages are included in a single transaction. This is done for all IBC datagrams that include proofs collected from the source chain.The messages that do not require proofs are:
MsgConnectionOpenInit
(conn-open-init
command),MsgChannelOpenInit
(chan-open-init
command),MsgChannelCloseInit
(chan-close-init
command) andMsgTransfer
(ft-transfer
command)
-
dst-chain-id
- is the identifier of the chain where the transaction will be sent. -
src-chain-id
- is the identifier of the chain that is queried for the data that is included in the transaction, e.g. connection data, client proofs, etc. To ensure correct on-chain state, the relayer also queries the destination chain, however it does not include this information in the Tx to the destination chain. -
dst-obj-id
- the identifier of an object on destination chain required by the datagram, e.g. theclient-id
associated with the connection on destination chain in connection datagrams. Or theconnection-id
in aConnOpenAck
datagram. -
src-obj-id
- the identifier of an object on the source chain, required by the datagram, e.d. theclient-id
of the connection on source chain. -
More details about the
tx
commands can be found in the following sections:
Usage
USAGE:
hermes tx <SUBCOMMAND>
DESCRIPTION:
Raw commands for sending transactions to a configured chain.
SUBCOMMANDS:
help Get usage information
conn-init Initialize a connection (ConnectionOpenInit)
conn-try Relay the connection attempt (ConnectionOpenTry)
conn-ack Relay acknowledgment of a connection attempt (ConnectionOpenAck)
conn-confirm Confirm opening of a connection (ConnectionOpenConfirm)
chan-open-init Initialize a channel (ChannelOpenInit)
chan-open-try Relay the channel attempt (ChannelOpenTry)
chan-open-ack Relay acknowledgment of a channel attempt (ChannelOpenAck)
chan-open-confirm Confirm opening of a channel (ChannelOpenConfirm)
chan-close-init Initiate the closing of a channel (ChannelCloseInit)
chan-close-confirm Confirm the closing of a channel (ChannelCloseConfirm)
ft-transfer Send a fungible token transfer test transaction (ICS20 MsgTransfer)
packet-recv Relay receive or timeout packets
packet-ack Relay acknowledgment packets
upgrade-chain Send an IBC upgrade plan
Connection Handshake
The tx
commands can be used to establish a connection between two clients.
sequenceDiagram autonumber participant A as ibc-1 participant B as ibc-0 Note over A, B: No connection A->>B: ConnectionOpenInit Note over B: connection: connection-0 Note over B: counterparty: none B->>A: ConnectionOpenTry Note over A: connection: connection-1 Note over A: counterparty: connection-0 A->>B: ConnectionOpenAck note over B: connection: connection-0 note over B: counterparty: connection-1 B->>A: ConnectionOpenConfirm Note over A, B: Connection open
Table of Contents
Connection Init
Use the conn-init
command to initialize a new connection on a chain.
USAGE:
hermes tx conn-init --dst-chain <DST_CHAIN_ID> --src-chain <SRC_CHAIN_ID> --dst-client <DST_CLIENT_ID> --src-client <SRC_CLIENT_ID>
DESCRIPTION:
Initialize a connection (ConnectionOpenInit)
REQUIRED:
--src-chain <SRC_CHAIN_ID> Identifier of the source chain
--src-client <SRC_CLIENT_ID> Identifier of the source client
--dst-chain <DST_CHAIN_ID> Identifier of the destination chain
--dst-client <DST_CLIENT_ID> Identifier of the destination client
Example
Given that two clients were previously created with identifier 07-tendermint-0
on chain ibc-0
and
identifier 07-tendermint-1
on chain ibc-1
, we can initialize a connection between the two clients.
First, let's initialize the connection on ibc-0
:
hermes tx conn-init --dst-chain ibc-0 --src-chain ibc-1 --dst-client 07-tendermint-0 --src-client 07-tendermint-1
Success: OpenInitConnection(
OpenInit(
Attributes {
height: Height {
revision: 0,
height: 73,
},
connection_id: Some(
ConnectionId(
"connection-0",
),
),
client_id: ClientId(
"07-tendermint-0",
),
counterparty_connection_id: None,
counterparty_client_id: ClientId(
"07-tendermint-1",
),
},
),
)
A new connection has been initialized on ibc-0
with identifier connection-0
.
Note that the
counterparty_connection_id
field is currently empty.
Connection Try
Use the conn-try
command to establish a counterparty to the connection on the other chain.
USAGE:
hermes tx conn-try [OPTIONS] --dst-chain <DST_CHAIN_ID> --src-chain <SRC_CHAIN_ID> --dst-client <DST_CLIENT_ID> --src-client <SRC_CLIENT_ID> --src-connection <SRC_CONNECTION_ID>
DESCRIPTION:
Relay the connection attempt (ConnectionOpenTry)
OPTIONS:
--dst-connection <DST_CONNECTION_ID>
Identifier of the destination connection (optional) [aliases: dst-conn]
REQUIRED:
--src-chain <SRC_CHAIN_ID>
Identifier of the source chain
--src-client <SRC_CLIENT_ID>
Identifier of the source client
--src-connection <SRC_CONNECTION_ID>
Identifier of the source connection (required) [aliases: src-conn]
--dst-chain <DST_CHAIN_ID>
Identifier of the destination chain
--dst-client <DST_CLIENT_ID>
Identifier of the destination client
Example
Let's now create the counterparty to connection-0
on chain ibc-1
:
hermes tx conn-try --dst-chain ibc-1 --src-chain ibc-0 --dst-client 07-tendermint-1 --src-client 07-tendermint-0 --src-connection connection-0
Success: OpenTryConnection(
OpenTry(
Attributes {
height: Height {
revision: 1,
height: 88,
},
connection_id: Some(
ConnectionId(
"connection-1",
),
),
client_id: ClientId(
"07-tendermint-1",
),
counterparty_connection_id: Some(
ConnectionId(
"connection-0",
),
),
counterparty_client_id: ClientId(
"07-tendermint-0",
),
},
),
)
A new connection has been created on ibc-1
with identifier connection-1
.
Note that the field
counterparty_connection_id
points to the connection onibc-0
.
Connection Ack
Use the conn-ack
command to acknowledge the connection on the initial chain.
USAGE:
hermes tx conn-ack --dst-chain <DST_CHAIN_ID> --src-chain <SRC_CHAIN_ID> --dst-client <DST_CLIENT_ID> --src-client <SRC_CLIENT_ID> --dst-connection <DST_CONNECTION_ID> --src-connection <SRC_CONNECTION_ID>
DESCRIPTION:
Relay acknowledgment of a connection attempt (ConnectionOpenAck)
REQUIRED:
--src-chain <SRC_CHAIN_ID>
Identifier of the source chain
--src-client <SRC_CLIENT_ID>
Identifier of the source client
--src-connection <SRC_CONNECTION_ID>
Identifier of the source connection (required) [aliases: src-conn]
--dst-chain <DST_CHAIN_ID>
Identifier of the destination chain
--dst-client <DST_CLIENT_ID>
Identifier of the destination client
--dst-connection <DST_CONNECTION_ID>
Identifier of the destination connection (required) [aliases: dst-conn]
Example
We can now acknowledge on ibc-0
that ibc-1
has accepted the connection attempt:
hermes tx conn-ack --dst-chain ibc-0 --src-chain ibc-1 --dst-client 07-tendermint-0 --src-client 07-tendermint-1 --dst-connection connection-0 --src-connection connection-1
Success: OpenAckConnection(
OpenAck(
Attributes {
height: Height {
revision: 0,
height: 206,
},
connection_id: Some(
ConnectionId(
"connection-0",
),
),
client_id: ClientId(
"07-tendermint-0",
),
counterparty_connection_id: Some(
ConnectionId(
"connection-1",
),
),
counterparty_client_id: ClientId(
"07-tendermint-1",
),
},
),
)
Note that the field
counterparty_connection_id
now points to the connection onibc-1
.
Connection Confirm
Use the conn-confirm
command to confirm that the connection has been acknowledged,
and finish the handshake, after which the connection is open on both chains.
USAGE:
hermes tx conn-confirm --dst-chain <DST_CHAIN_ID> --src-chain <SRC_CHAIN_ID> --dst-client <DST_CLIENT_ID> --src-client <SRC_CLIENT_ID> --dst-connection <DST_CONNECTION_ID> --src-connection <SRC_CONNECTION_ID>
DESCRIPTION:
Confirm opening of a connection (ConnectionOpenConfirm)
REQUIRED:
--src-chain <SRC_CHAIN_ID>
Identifier of the source chain
--src-client <SRC_CLIENT_ID>
Identifier of the source client
--src-connection <SRC_CONNECTION_ID>
Identifier of the source connection (required) [aliases: src-conn]
--dst-chain <DST_CHAIN_ID>
Identifier of the destination chain
--dst-client <DST_CLIENT_ID>
Identifier of the destination client
--dst-connection <DST_CONNECTION_ID>
Identifier of the destination connection (required) [aliases: dst-conn]
Example
Confirm on ibc-1
that ibc-0
has accepted the connection attempt.
hermes tx conn-confirm --dst-chain ibc-1 --src-chain ibc-0 --dst-client 07-tendermint-1 --src-client 07-tendermint-0 --dst-connection connection-1 --src-connection connection-0
Success: OpenConfirmConnection(
OpenConfirm(
Attributes {
height: Height {
revision: 1,
height: 239,
},
connection_id: Some(
ConnectionId(
"connection-1",
),
),
client_id: ClientId(
"07-tendermint-1",
),
counterparty_connection_id: Some(
ConnectionId(
"connection-0",
),
),
counterparty_client_id: ClientId(
"07-tendermint-0",
),
},
),
)
We have now successfully established a connection between the two chains.
Channel Open Handshake
The tx
commands can be used to establish a channel for a given connection. Only unordered
channels are currently supported.
sequenceDiagram autonumber participant A as ibc-1 participant B as ibc-0 Note over A, B: No channel A->>B: ChannelOpenInit Note over B: channel: channel-0 Note over B: channel: counterparty: none B->>A: ChannelOpenTry Note over A: channel: channel-1 Note over A: channel: counterparty: channel-0 A->>B: ChannelOpenAck note over B: channel: channel-0 note over B: counterparty: channel-1 B->>A: ChannelOpenConfirm Note over A, B: Channel open
Table of Contents
Channel Open Init
Use the chan-open-init
command to initialize a new channel.
USAGE:
hermes tx chan-open-init [OPTIONS] --dst-chain <DST_CHAIN_ID> --src-chain <SRC_CHAIN_ID> --dst-connection <DST_CONNECTION_ID> --dst-port <DST_PORT_ID> --src-port <SRC_PORT_ID>
DESCRIPTION:
Initialize a channel (ChannelOpenInit)
OPTIONS:
--order <ORDER> The channel ordering, valid options 'unordered' (default) and 'ordered'
[default: ORDER_UNORDERED]
REQUIRED:
--src-chain <SRC_CHAIN_ID>
Identifier of the source chain
--src-port <SRC_PORT_ID>
Identifier of the source port
--dst-chain <DST_CHAIN_ID>
Identifier of the destination chain
--dst-connection <DST_CONNECTION_ID>
Identifier of the destination connection [aliases: dst-conn]
--dst-port <DST_PORT_ID>
Identifier of the destination port
Example
First, let's initialize the channel on ibc-0
using an existing connection identified by connection-0
:
hermes tx chan-open-init --dst-chain ibc-0 --src-chain ibc-1 --dst-connection connection-0 --dst-port transfer --src-port transfer
Success: OpenInitChannel(
OpenInit(
Attributes {
height: Height {
revision: 0,
height: 3091
},
port_id: PortId(
"transfer",
),
channel_id: Some(
ChannelId(
"channel-0",
),
),
connection_id: ConnectionId(
"connection-0",
),
counterparty_port_id: PortId(
"transfer",
),
counterparty_channel_id: None,
},
),
)
A new channel has been initialized on ibc-1
with identifier channel-0
.
Note that the
counterparty_channel_id
field is currently empty.
Channel Open Try
Use the chan-open-try
command to establish a counterparty to the channel on the other chain.
USAGE:
hermes tx chan-open-try [OPTIONS] --dst-chain <DST_CHAIN_ID> --src-chain <SRC_CHAIN_ID> --dst-connection <DST_CONNECTION_ID> --dst-port <DST_PORT_ID> --src-port <SRC_PORT_ID> --src-channel <SRC_CHANNEL_ID>
DESCRIPTION:
Relay the channel attempt (ChannelOpenTry)
OPTIONS:
--dst-channel <DST_CHANNEL_ID> Identifier of the destination channel (optional) [aliases:
b-chan]
REQUIRED:
--src-chain <SRC_CHAIN_ID>
Identifier of the source chain
--src-channel <SRC_CHANNEL_ID>
Identifier of the source channel (required) [aliases: src-chan]
--src-port <SRC_PORT_ID>
Identifier of the source port
--dst-chain <DST_CHAIN_ID>
Identifier of the destination chain
--dst-connection <DST_CONNECTION_ID>
Identifier of the destination connection [aliases: dst-conn]
--dst-port <DST_PORT_ID>
Identifier of the destination port
Example
Let's now create the counterparty to channel-0
on chain ibc-1
:
hermes tx chan-open-try --dst-chain ibc-1 --src-chain ibc-0 --dst-connection connection-1 --dst-port transfer --src-port transfer --src-channel channel-0
Success: OpenTryChannel(
OpenTry(
Attributes {
height: Height {
revision: 1,
height: 3213
},
port_id: PortId(
"transfer",
),
channel_id: Some(
ChannelId(
"channel-1",
),
),
connection_id: ConnectionId(
"connection-1",
),
counterparty_port_id: PortId(
"transfer",
),
counterparty_channel_id: Some(
ChannelId(
"channel-0",
),
),
},
),
)
A new channel has been created on ibc-1
with identifier channel-1
.
Note that the field
counterparty_channel_id
points to the channel onibc-0
.
Channel Open Ack
Use the chan-open-ack
command to acknowledge the channel on the initial chain.
USAGE:
hermes tx chan-open-ack --dst-chain <DST_CHAIN_ID> --src-chain <SRC_CHAIN_ID> --dst-connection <DST_CONNECTION_ID> --dst-port <DST_PORT_ID> --src-port <SRC_PORT_ID> --dst-channel <DST_CHANNEL_ID> --src-channel <SRC_CHANNEL_ID>
DESCRIPTION:
Relay acknowledgment of a channel attempt (ChannelOpenAck)
REQUIRED:
--src-chain <SRC_CHAIN_ID>
Identifier of the source chain
--src-channel <SRC_CHANNEL_ID>
Identifier of the source channel (required) [aliases: src-chan]
--src-port <SRC_PORT_ID>
Identifier of the source port
--dst-chain <DST_CHAIN_ID>
Identifier of the destination chain
--dst-channel <DST_CHANNEL_ID>
Identifier of the destination channel (required) [aliases: dst-chan]
--dst-connection <DST_CONNECTION_ID>
Identifier of the destination connection [aliases: dst-conn]
--dst-port <DST_PORT_ID>
Identifier of the destination port
Example
We can now acknowledge on ibc-0
that ibc-1
has accepted the opening of the channel:
hermes tx chan-open-ack --dst-chain ibc-0 --src-chain ibc-1 --dst-connection connection-0 --dst-port transfer --src-port transfer --dst-channel channel-0 --src-channel channel-1
Success: OpenAckChannel(
OpenAck(
Attributes {
height: Height {
revision: 0,
height: 3301
},
port_id: PortId(
"transfer",
),
channel_id: Some(
ChannelId(
"channel-0",
),
),
connection_id: ConnectionId(
"connection-0",
),
counterparty_port_id: PortId(
"transfer",
),
counterparty_channel_id: Some(
ChannelId(
"channel-1",
),
),
},
),
)
Note that the field
counterparty_channel_id
now points to the channel onibc-1
.
Channel Open Confirm
Use the chan-open-confirm
command to confirm that the channel has been acknowledged,
and finish the handshake, after which the channel is open on both chains.
USAGE:
hermes tx chan-open-confirm --dst-chain <DST_CHAIN_ID> --src-chain <SRC_CHAIN_ID> --dst-connection <DST_CONNECTION_ID> --dst-port <DST_PORT_ID> --src-port <SRC_PORT_ID> --dst-channel <DST_CHANNEL_ID> --src-channel <SRC_CHANNEL_ID>
DESCRIPTION:
Confirm opening of a channel (ChannelOpenConfirm)
REQUIRED:
--src-chain <SRC_CHAIN_ID>
Identifier of the source chain
--src-channel <SRC_CHANNEL_ID>
Identifier of the source channel (required) [aliases: src-chan]
--src-port <SRC_PORT_ID>
Identifier of the source port
--dst-chain <DST_CHAIN_ID>
Identifier of the destination chain
--dst-channel <DST_CHANNEL_ID>
Identifier of the destination channel (required) [aliases: dst-chan]
--dst-connection <DST_CONNECTION_ID>
Identifier of the destination connection [aliases: dst-conn]
--dst-port <DST_PORT_ID>
Identifier of the destination port
Example
Confirm on ibc-1
that ibc-0
has accepted the opening of the channel,
after which the channel is open on both chains.
hermes tx chan-open-confirm --dst-chain ibc-1 --src-chain ibc-0 --dst-connection connection-1 --dst-port transfer --src-port transfer --dst-channel channel-1 --src-channel channel-0
OpenConfirm(
Attributes {
height: Height {
revision: 1,
height: 3483
},
port_id: PortId(
"transfer",
),
channel_id: Some(
ChannelId(
"channel-1",
),
),
connection_id: ConnectionId(
"connection-1",
),
counterparty_port_id: PortId(
"transfer",
),
counterparty_channel_id: Some(
ChannelId(
"channel-0",
),
),
},
),
)
We have now successfully opened a channel over an existing connection between the two chains.
Channel Close Handshake
The channel close handshake involves two steps: init and confirm.
Table of Contents
Channel Close Init
Use the chan-close-init
command to initialize the closure of a channel.
USAGE:
hermes tx chan-close-init --dst-chain <DST_CHAIN_ID> --src-chain <SRC_CHAIN_ID> --dst-connection <DST_CONNECTION_ID> --dst-port <DST_PORT_ID> --src-port <SRC_PORT_ID> --dst-channel <DST_CHANNEL_ID> --src-channel <SRC_CHANNEL_ID>
DESCRIPTION:
Initiate the closing of a channel (ChannelCloseInit)
REQUIRED:
--src-chain <SRC_CHAIN_ID>
Identifier of the source chain
--src-channel <SRC_CHANNEL_ID>
Identifier of the source channel (required) [aliases: src-chan]
--src-port <SRC_PORT_ID>
Identifier of the source port
--dst-chain <DST_CHAIN_ID>
Identifier of the destination chain
--dst-channel <DST_CHANNEL_ID>
Identifier of the destination channel (required) [aliases: dst-chan]
--dst-connection <DST_CONNECTION_ID>
Identifier of the destination connection [aliases: dst-conn]
--dst-port <DST_PORT_ID>
Identifier of the destination port
Example
hermes tx chan-close-init --dst-chain ibc-0 --src-chain ibc-1 --dst-connection connection-0 --dst-port transfer --src-port transfer --dst-channel channel-0 --src-channel channel-1
Success: CloseInitChannel(
CloseInit(
Attributes {
height: Height {
revision: 0,
height: 77,
},
port_id: PortId(
"transfer",
),
channel_id: Some(
ChannelId(
"channel-0",
),
),
connection_id: ConnectionId(
"connection-0",
),
counterparty_port_id: PortId(
"transfer",
),
counterparty_channel_id: Some(
ChannelId(
"channel-1",
),
),
},
),
)
Channel Close Confirm
Use the chan-close-confirm
command to confirm the closure of a channel.
USAGE:
hermes tx chan-close-confirm --dst-chain <DST_CHAIN_ID> --src-chain <SRC_CHAIN_ID> --dst-connection <DST_CONNECTION_ID> --dst-port <DST_PORT_ID> --src-port <SRC_PORT_ID> --dst-channel <DST_CHANNEL_ID> --src-channel <SRC_CHANNEL_ID>
DESCRIPTION:
Confirm the closing of a channel (ChannelCloseConfirm)
REQUIRED:
--src-chain <SRC_CHAIN_ID>
Identifier of the source chain
--src-channel <SRC_CHANNEL_ID>
Identifier of the source channel (required) [aliases: src-chan]
--src-port <SRC_PORT_ID>
Identifier of the source port
--dst-chain <DST_CHAIN_ID>
Identifier of the destination chain
--dst-channel <DST_CHANNEL_ID>
Identifier of the destination channel (required) [aliases: dst-chan]
--dst-connection <DST_CONNECTION_ID>
Identifier of the destination connection [aliases: dst-conn]
--dst-port <DST_PORT_ID>
Identifier of the destination port
Example
hermes tx chan-close-confirm --dst-chain ibc-1 --src-chain ibc-0 --dst-connection connection-1 --src-port transfer --dst-port transfer --dst-channel channel-1 --src-channel channel-0
Success: CloseConfirmChannel(
CloseConfirm(
Attributes {
height: Height {
revision: 1,
height: 551,
},
port_id: PortId(
"transfer",
),
channel_id: Some(
ChannelId(
"channel-1",
),
),
connection_id: ConnectionId(
"connection-1",
),
counterparty_port_id: PortId(
"transfer",
),
counterparty_channel_id: Some(
ChannelId(
"channel-0",
),
),
},
),
)
NOTE: The cosmos-sdk
transfer module implementation does not allow the user (hermes
in this case) to initiate the closing of channels.
Therefore, when using the Gaia release image, the chan-close-init
command
fails as the MsgChannelCloseInit
message included in the transaction is rejected.
To be able to test channel closure, you need to patch your gaia deployments.
Packet Tx Commands
Table of Contents
Fungible token transfer
Use the tx ft-transfer
command to send ICS-20 fungible token transfer packets.
NOTE: This command is mainly used for testing the packet features of the relayer.
USAGE:
hermes tx ft-transfer [OPTIONS] --dst-chain <DST_CHAIN_ID> --src-chain <SRC_CHAIN_ID> --src-port <SRC_PORT_ID> --src-channel <SRC_CHANNEL_ID> --amount <AMOUNT>
OPTIONS:
--denom <DENOM>
Denomination of the coins to send [default: samoleans]
-h, --help
Print help information
--key-name <KEY_NAME>
Use the given signing key name (default: `key_name` config)
--number-msgs <NUMBER_MSGS>
Number of messages to send
--receiver <RECEIVER>
The account address on the destination chain which will receive the tokens. If omitted,
the relayer's wallet on the destination chain will be used
--timeout-height-offset <TIMEOUT_HEIGHT_OFFSET>
Timeout in number of blocks since current [default: 0]
--timeout-seconds <TIMEOUT_SECONDS>
Timeout in seconds since current [default: 0]
REQUIRED:
--amount <AMOUNT>
Amount of coins (samoleans, by default) to send (e.g. `100000`)
--dst-chain <DST_CHAIN_ID>
Identifier of the destination chain
--src-chain <SRC_CHAIN_ID>
Identifier of the source chain
--src-channel <SRC_CHANNEL_ID>
Identifier of the source channel [aliases: src-chan]
--src-port <SRC_PORT_ID>
Identifier of the source port
Example
Send two transfer packets from the transfer
module and channel-0
of ibc-0
to ibc-1
. Each transfer if for 9999
samoleans (default denomination) and a timeout offset of 10
blocks. The transfer fee is paid by the relayer account on ibc-1
.
hermes tx ft-transfer --dst-chain ibc-1 --src-chain ibc-0 --src-port transfer --src-channel channel-0 --amount 9999 --timeout-height-offset 1000 --number-msgs 2
Success: [
SendPacket(
SendPacket {
height: Height {
revision: 0,
height: 431,
},
packet: PortId("transfer") ChannelId("channel-0") Sequence(4),
},
),
SendPacket(
SendPacket {
height: Height {
revision: 0,
height: 431,
},
packet: PortId("transfer") ChannelId("channel-0") Sequence(5),
},
),
]
The transfer packets are stored on ibc-0
and can be relayed.
To send transfer packets with a custom receiver address use the
--receiver
flag.
hermes tx ft-transfer --dst-chain ibc-1 --src-chain ibc-0 --src-port transfer --src-channel channel-0 --amount 9999 --timeout-height-offset 1000 --number-msgs 1 --receiver board:1938586739
Success: [
SendPacket(
SendPacket {
height: Height {
revision: 0,
height: 546,
},
packet: PortId("transfer") ChannelId("channel-0") Sequence(7),
},
),
]
Relay receive and timeout packets
Use the tx packet-recv
command to relay the packets sent but not yet received. If the sent packets have timed out then a timeout packet is sent to the source chain.
USAGE:
hermes tx packet-recv --reference-chain <REFERENCE_CHAIN_ID> --host-chain <HOST_CHAIN_ID> --host-port <HOST_PORT_ID> --host-channel <HOST_CHANNEL_ID>
DESCRIPTION:
Relay receive or timeout packets
REQUIRED:
--reference-chain <REFERENCE_CHAIN_ID>
Identifier of the destination chain
--host-chain <HOST_CHAIN_ID>
Identifier of the source chain
--host-channel <HOST_CHANNEL_ID>
Identifier of the source channel [aliases: sender-chan]
--host-port <HOST_PORT_ID>
Identifier of the source port
Example
Send the two transfer packets to the ibc-1
module bound to the transfer
port and the channel-0
's counterparty.
NOTE: The relayer prepends a client update message before the receive messages.
hermes tx packet-recv --reference-chain ibc-1 --host-chain ibc-0 --host-port transfer --host-channel channel-0
Success: [
UpdateClient(
UpdateClient {
common: Attributes {
height: Height {
revision: 1,
height: 439,
},
client_id: ClientId(
"07-tendermint-1",
),
client_type: Tendermint,
consensus_height: Height {
revision: 0,
height: 449,
},
},
header: Some(
Tendermint(...),
),
},
),
WriteAcknowledgement(
WriteAcknowledgement {
height: Height {
revision: 1,
height: 439,
},
packet: PortId("transfer") ChannelId("channel-0") Sequence(4),
ack: [
123,
34,
114,
101,
115,
117,
108,
116,
34,
58,
34,
65,
81,
61,
61,
34,
125,
],
},
),
WriteAcknowledgement(
WriteAcknowledgement {
height: Height {
revision: 1,
height: 439,
},
packet: PortId("transfer") ChannelId("channel-0") Sequence(5),
ack: [
123,
34,
114,
101,
115,
117,
108,
116,
34,
58,
34,
65,
81,
61,
61,
34,
125,
],
},
),
]
Both packets have been relayed to ibc-1
and acknowledged.
Relay acknowledgment packets
Use the tx packet-ack
command to relay acknowledgments to the original source of the packets.
USAGE:
hermes tx packet-ack --reference-chain <REFERENCE_CHAIN_ID> --host-chain <HOST_CHAIN_ID> --host-port <HOST_PORT_ID> --host-channel <HOST_CHANNEL_ID>
DESCRIPTION:
Relay acknowledgment packets
REQUIRED:
--reference-chain <REFERENCE_CHAIN_ID>
Identifier of the destination chain
--host-chain <HOST_CHAIN_ID>
Identifier of the source chain
--host-channel <HOST_CHANNEL_ID>
Identifier of the source channel [aliases: sender-chan]
--host-port <HOST_PORT_ID>
Identifier of the source port
Example
Send the acknowledgments to the ibc-0
module bound to the transfer
port and the channel-1
's counterparty.
NOTE: The relayer prepends a client update message before the acknowledgments.
hermes tx packet-ack --reference-chain ibc-0 --host-chain ibc-1 --host-port transfer --host-channel channel-1
Success: [
UpdateClient(
UpdateClient {
common: Attributes {
height: Height {
revision: 0,
height: 495,
},
client_id: ClientId(
"07-tendermint-0",
),
client_type: Tendermint,
consensus_height: Height {
revision: 1,
height: 483,
},
},
header: Some(
Tendermint(...),
),
},
),
AcknowledgePacket(
AcknowledgePacket {
height: Height {
revision: 0,
height: 495,
},
packet: PortId("transfer") ChannelId("channel-0") Sequence(4),
},
),
AcknowledgePacket(
AcknowledgePacket {
height: Height {
revision: 0,
height: 495,
},
packet: PortId("transfer") ChannelId("channel-0") Sequence(5),
},
),
]
Both acknowledgments have been received on ibc-0
.
Upgrade Tx Commands
Table of Contents
Upgrade Chain
Use this to make an upgrade proposal.
USAGE:
hermes tx upgrade-chain [OPTIONS] --reference-chain <REFERENCE_CHAIN_ID> --host-chain <HOST_CHAIN_ID> --host-client <HOST_CLIENT_ID> --amount <AMOUNT> --height-offset <HEIGHT_OFFSET>
DESCRIPTION:
Send an IBC upgrade plan
OPTIONS:
--denom <DENOM>
Denomination for the deposit (default: 'stake')
--new-chain <CHAIN_ID>
New chain identifier to assign to the upgrading chain (optional)
--new-unbonding <UNBONDING_PERIOD>
New unbonding period to assign to the upgrading chain, in seconds (optional)
--upgrade-name <UPGRADE_NAME>
A string to name the upgrade proposal plan (default: 'plan')
REQUIRED:
--amount <AMOUNT>
Amount of stake
--height-offset <HEIGHT_OFFSET>
Upgrade height offset in number of blocks since current
--reference-chain <REFERENCE_CHAIN_ID>
Identifier of the chain to upgrade
--host-chain <HOST_CHAIN_ID>
Identifier of the source chain
--host-client <HOST_CLIENT_ID>
Identifier of the client on source chain from which the plan is created
Example
An upgrade proposal is made for ibc-0
, for height 300
blocks from latest height, with 10000000stake
deposited. The proposal will include the upgraded client state constructed from the state of 07-tendermint-0
client on ibc-1
.
hermes tx upgrade-chain --reference-chain ibc-0 --host-chain ibc-1 --host-client 07-tendermint-0 --amount 10000000 --height-offset 300
Success: transaction::Hash(779713508B6103E37FADE60483BEE964A90BD67E5F20037B2CC4AE0E90B707C3)
Help
This section provides guidelines regarding troubleshooting and general
resources for getting help with hermes
.
For this purpose, we recommend a few ideas that could be of help:
- hermes help command, providing a CLI
documentation for all
hermes
commands. - profile your relayer binary to identify slow methods;
- configure the
log_level
to help with debugging; - patch your local gaia chain(s) to enable some corner-case methods (e.g., channel close);
And if the above options do not address your specific problem:
- you can request a new feature;
- or consult the list of reported issues and search by relevant keywords to see if you're dealing with a known problem;
- we would be grateful if you can submit a bug report discussing any problem you find, and from there on we can look at the problem together;
Lastly, for general questions, you can reach us at hello@informal.systems
,
or on Twitter @informalinc.
Table of contents
- Help command
- Parametrizing the log output level
- Overriding the tracing filter using
RUST_LOG
- Inspecting the relayer state
- Connecting to a full node protected by HTTP Basic Authentication
- Patching
gaia
to supportChanCloseInit
- New Feature Request
- Profiling
Help command
The CLI comprises a special help
command, which accepts as parameter other commands, and provides guidance on what is the correct way to invoke those commands.
NOTE: This special
help
command is preferred as it will display the full help message.
For instance,
hermes help create
will provide details about all the valid invocations of the create
CLI command.
USAGE:
hermes create <SUBCOMMAND>
DESCRIPTION:
Create objects (client, connection, or channel) on chains
SUBCOMMANDS:
channel Create a new channel between two chains
client Create a new IBC client
connection Create a new connection between two chains
help Print this message or the help of the given subcommand(s)
This can provide further specific guidance if we add additional parameters, e.g.,
hermes help create channel
USAGE:
hermes create channel [OPTIONS] --a-chain <A_CHAIN_ID> --a-connection <A_CONNECTION_ID> --a-port <A_PORT_ID> --b-port <B_PORT_ID>
hermes create channel [OPTIONS] --a-chain <A_CHAIN_ID> --b-chain <B_CHAIN_ID> --a-port <A_PORT_ID> --b-port <B_PORT_ID> --new-client-connection
DESCRIPTION:
Create a new channel between two chains.
Can create a new channel using a pre-existing connection or alternatively, create a new client and a
new connection underlying the new channel if a pre-existing connection is not provided.
OPTIONS:
--channel-version <VERSION>
The version for the new channel
[aliases: chan-version]
--new-client-connection
Indicates that a new client and connection will be created underlying the new channel
[aliases: new-client-conn]
--order <ORDER>
The channel ordering, valid options 'unordered' (default) and 'ordered'
[default: ORDER_UNORDERED]
--yes
Skip new_client_connection confirmation
FLAGS:
--a-chain <A_CHAIN_ID>
Identifier of the side `a` chain for the new channel
--a-connection <A_CONNECTION_ID>
Identifier of the connection on chain `a` to use in creating the new channel
[aliases: a-conn]
--a-port <A_PORT_ID>
Identifier of the side `a` port for the new channel
--b-chain <B_CHAIN_ID>
Identifier of the side `b` chain for the new channel
--b-port <B_PORT_ID>
Identifier of the side `b` port for the new channel
Additionally, the -h
/--help
flags typical for CLI applications work on
all commands.
Parametrizing the log output level
The relayer configuration file permits parametrization of output verbosity via the knob called log_level
.
This file is loaded by default from $HOME/.hermes/config.toml
, but can be overridden in all commands
with the --config
flag, eg. hermes --config ./path/to/my/config.toml some command
.
Relevant snippet:
[global]
log_level = 'error'
Valid options for log_level
are: 'error', 'warn', 'info', 'debug', 'trace'.
These levels correspond to the tracing sub-component of the relayer-cli,
see here.
The relayer will always print a last line summarizing the result of its operation for queries or transactions. In addition to this last line, arbitrary debug, info, or other outputs may be produced.
Overriding the tracing filter using RUST_LOG
For debugging purposes, we may want to inspect which RPC queries the relayer is making.
The relayer makes use of the tendermint-rpc
library to issue RPC queries, but
the output of this library is by default turned off in order to keep the logs more
readable.
Using the RUST_LOG
environment variable, we can turn logging on for the
tendermint-rpc
library, as follows:
RUST_LOG=tendermint-rpc=debug,info hermes start
Setting the RUST_LOG
environment variable to tendermint_rpc=debug,info
instructs
the relayer to set the log level of the tendermint_rpc
crate to debug
and otherwise
use the info
log level.
Note: While the
tendermint-rpc
contains a dash in its name, the logging filter expects a module name, which can only contain alphanumeric characters and underscores, hence why the filter above is writtentendermint_rpc=debug
.
Example:
❯ RUST_LOG=tendermint_rpc=debug,info hermes start
2022-02-24T14:32:14.039555Z INFO ThreadId(01) using default configuration from '/Users/coromac/.hermes/config.toml'
2022-02-24T14:32:14.043500Z INFO ThreadId(01) telemetry service running, exposing metrics at http://127.0.0.1:3001/metrics
2022-02-24T14:32:14.043542Z INFO ThreadId(01) [rest] address not configured, REST server disabled
2022-02-24T14:32:14.049759Z DEBUG ThreadId(01) Incoming response: {
"jsonrpc": "2.0",
"id": "143b4580-c49e-47c1-81b2-4e7090f6e762",
"result": {
"node_info": {
"protocol_version": {
"p2p": "8",
"block": "11",
"app": "0"
},
"id": "73f9134539f9845cd253dc302e36d48ee4c0f32d",
"listen_addr": "tcp://0.0.0.0:27003",
"network": "ibc0",
"version": "v0.34.14",
"channels": "40202122233038606100",
"moniker": "ibc0",
"other": {
"tx_index": "on",
"rpc_address": "tcp://0.0.0.0:27000"
}
},
"sync_info": {
"latest_block_hash": "8396B93E355AD80EED8167A04BB9858A315A8BEB482547DE16A6CD82BC11551B",
"latest_app_hash": "22419E041D6997EE75FF66F7F537A3D36122B220EAB89A9C246FEF680FB1C97A",
"latest_block_height": "86392",
"latest_block_time": "2022-02-24T14:32:08.673989Z",
"earliest_block_hash": "0A73CFE8566D4D4FBFE3178D9BCBAD483FD689854CA8012FF1457F8EC4598132",
"earliest_app_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
"earliest_block_height": "1",
"earliest_block_time": "2022-01-20T09:04:21.549736Z",
"catching_up": false
},
"validator_info": {
"address": "6FD56E6AA1EEDAD227AFAB6B9DE631719D4A3691",
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "mR5V/QWOv/mJYyNmlsl3mfxKy1PNaOzdztyas4NF2BA="
},
"voting_power": "10"
}
}
}
2022-02-24T14:32:14.052503Z DEBUG ThreadId(21) Incoming response: {
"jsonrpc": "2.0",
"id": "0ca35e64-ea98-4fbf-bd66-c3291128ace9",
"result": {}
}
...
The two DEBUG log lines above were emitted by the tendermint-rpc
crate.
Inspecting the relayer state
To get a little bit of insight into the state of the relayer,
Hermes will react to a SIGUSR1
signal by dumping its state to
the console, either in plain text form or as a JSON object if Hermes
was started with the --json
option.
To send a SIGUSR1
signal to Hermes, look up its process ID (below PID)
and use the following command:
kill -SIGUSR1 PID
Hermes will print some information about the workers which are currently running.
For example, with three chains configured and one channel between each pair of chains:
INFO Dumping state (triggered by SIGUSR1)
INFO
INFO * Chains: ibc-0, ibc-1, ibc-2
INFO * Client workers:
INFO - client::ibc-0->ibc-1:07-tendermint-0 (id: 5)
INFO - client::ibc-0->ibc-2:07-tendermint-0 (id: 9)
INFO - client::ibc-1->ibc-0:07-tendermint-0 (id: 1)
INFO - client::ibc-1->ibc-2:07-tendermint-1 (id: 11)
INFO - client::ibc-2->ibc-0:07-tendermint-1 (id: 3)
INFO - client::ibc-2->ibc-1:07-tendermint-1 (id: 7)
INFO * Packet workers:
INFO - packet::channel-0/transfer:ibc-0->ibc-1 (id: 2)
INFO - packet::channel-0/transfer:ibc-1->ibc-0 (id: 6)
INFO - packet::channel-0/transfer:ibc-2->ibc-0 (id: 10)
INFO - packet::channel-1/transfer:ibc-0->ibc-2 (id: 4)
INFO - packet::channel-1/transfer:ibc-1->ibc-2 (id: 8)
INFO - packet::channel-1/transfer:ibc-2->ibc-1 (id: 12)
or in JSON form (prettified):
{
"timestamp": "Jul 12 17:04:37.244",
"level": "INFO",
"fields": {
"message": "Dumping state (triggered by SIGUSR1)"
}
}
{
"chains": [
"ibc-0",
"ibc-1",
"ibc-2"
],
"workers": {
"Client": [
{
"id": 5,
"object": {
"type": "Client",
"dst_chain_id": "ibc-1",
"dst_client_id": "07-tendermint-0",
"src_chain_id": "ibc-0"
}
},
{
"id": 9,
"object": {
"type": "Client",
"dst_chain_id": "ibc-2",
"dst_client_id": "07-tendermint-0",
"src_chain_id": "ibc-0"
}
},
{
"id": 1,
"object": {
"type": "Client",
"dst_chain_id": "ibc-0",
"dst_client_id": "07-tendermint-0",
"src_chain_id": "ibc-1"
}
},
{
"id": 11,
"object": {
"type": "Client",
"dst_chain_id": "ibc-2",
"dst_client_id": "07-tendermint-1",
"src_chain_id": "ibc-1"
}
},
{
"id": 3,
"object": {
"type": "Client",
"dst_chain_id": "ibc-0",
"dst_client_id": "07-tendermint-1",
"src_chain_id": "ibc-2"
}
},
{
"id": 7,
"object": {
"type": "Client",
"dst_chain_id": "ibc-1",
"dst_client_id": "07-tendermint-1",
"src_chain_id": "ibc-2"
}
}
],
"Packet": [
{
"id": 2,
"object": {
"type": "Packet",
"dst_chain_id": "ibc-1",
"src_chain_id": "ibc-0",
"src_channel_id": "channel-0",
"src_port_id": "transfer"
}
},
{
"id": 6,
"object": {
"type": "Packet",
"dst_chain_id": "ibc-0",
"src_chain_id": "ibc-1",
"src_channel_id": "channel-0",
"src_port_id": "transfer"
}
},
{
"id": 10,
"object": {
"type": "Packet",
"dst_chain_id": "ibc-0",
"src_chain_id": "ibc-2",
"src_channel_id": "channel-0",
"src_port_id": "transfer"
}
},
{
"id": 4,
"object": {
"type": "Packet",
"dst_chain_id": "ibc-2",
"src_chain_id": "ibc-0",
"src_channel_id": "channel-1",
"src_port_id": "transfer"
}
},
{
"id": 8,
"object": {
"type": "Packet",
"dst_chain_id": "ibc-2",
"src_chain_id": "ibc-1",
"src_channel_id": "channel-1",
"src_port_id": "transfer"
}
},
{
"id": 12,
"object": {
"type": "Packet",
"dst_chain_id": "ibc-1",
"src_chain_id": "ibc-2",
"src_channel_id": "channel-1",
"src_port_id": "transfer"
}
}
]
}
}
Connecting to a full node protected by HTTP Basic Authentication
To connect to a full node protected by HTTP Basic Authentication,
specify the username and password in the rpc_addr
, grpc_addr
and websocket_addr
settings
under the chain configuration in config.toml
.
Here is an example with username hello
and password world
, assuming the RPC, WebSocket and gRPC servers
listen on domain mydomain.com
with TLS enabled (HTTPS/WSS).
[[chains]]
id = 'my-chain-0'
# ...
rpc_addr = 'https://hello:world@mydomain.com:26657'
grpc_addr = 'https://hello:world@mydomain.com:9090'
websocket_addr = 'wss://hello:world@mydomain.com:26657/websocket'
# ...
Caution: Warning: The "Basic" authentication scheme sends the credentials encoded but not encrypted. This would be completely insecure unless the exchange was over a secure connection (HTTPS/TLS).
Patching gaia
to support ChanCloseInit
The guide below refers specifically to patching your gaia chain so that the
relayer can initiate the closing of channels by submitting a ChanCloseInit
message.
Without this modification, the transaction will be rejected.
We also describe how to test the channel closing feature.
-
Clone the Cosmos SDK
git clone https://github.com/cosmos/cosmos-sdk.git ~/go/src/github.com/cosmos/cosmos-sdk cd ~/go/src/github.com/cosmos/cosmos-sdk
-
Apply these diffs:
--- a/x/ibc/applications/transfer/module.go +++ b/x/ibc/applications/transfer/module.go @@ -305,7 +305,7 @@ func (am AppModule) OnChanCloseInit( channelID string, ) error { // Disallow user-initiated channel closing for transfer channels - return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "user cannot close channel") + return nil }
-
Append the line below (watch for the placeholder
<your>
) as the last line in yourgo.mod
in the gaia clone:
replace github.com/cosmos/cosmos-sdk => /Users/<your>/go/src/github.com/cosmos/cosmos-sdk
- Now
make build
andmake install
your local copy of gaia
In order to test the correct operation during the channel close, perform the steps below.
-
the channel should be in state open-open:
-
transfer of 5555 samoleans from
ibc-1
toibc-0
. This results in a Tx toibc-1
for aMsgTransfer
packet. Make sure you're not relaying this packet (the relayer should not be running on this path).hermes tx ft-transfer --receiver-chain ibc-0 --sender-chain ibc-1 --sender-port transfer --sender-channel channel-1 --amount 5555 --timeout-height-offset 1000 --number-msgs 1 --denom samoleans
-
now do the first step of channel closing: the channel will transition to close-open:
hermes --config config.toml tx chan-close-init --receiver-chain ibc-0 --sender-chain ibc-1 --receiver-connection connection-0 --receiver-port transfer --sender-port transfer --receiver-channel channel-0 --sender-channel channel-1
-
trigger timeout on close to ibc-1
hermes --config config.toml tx packet-recv --receiver-chain ibc-0 --sender-chain ibc-1 --sender-port transfer --sender-channel channel-1
-
close-close
hermes --config config.toml tx chan-close-confirm --receiver-chain ibc-1 --sender-chain ibc-0 --receiver-connection connection-1 --receiver-port transfer --sender-port transfer --receiver-channel channel-1 --sender-channel channel-0
-
verify that the two ends are in Close state:
hermes --config config.toml query channel end --chain ibc-0 --port transfer --channel channel-0 hermes --config config.toml query channel end --chain ibc-1 --port transfer --channel channel-1
New Feature Request
If you would like a feature to be added to hermes
, don't hesitate
to open a discussion about that via the feature request
issue template.
Note that Hermes is packaged as part of the
ibc-relayer-cli
crate.
Profiling
The relayer
crate provides a time!
macro which can be used to measure how much time is spent between the invocation of the macro and the end of the enclosing scope.
Setup
The time!
macro has no effect unless the profiling
feature of the relayer
crate is enabled.
To enable it, one must compile the relayer-cli
crate with the --features=profiling
flag.
a) One way is to build the relayer
binary and update the hermes
alias to point to the executable:
cd relayer-cli/
cargo build --features=profiling
b) Alternatively, one can use the cargo run
command and update the alias accordingly:
alias hermes='cargo run --features=profiling --manifest-path=relayer-cli/Cargo.toml --'
The --manifest-path=relayer-cli/Cargo.toml
flag is needed for cargo run
to accept the --features
flag.
Example
#![allow(unused)] fn main() { fn my_function(x: u32) -> u32 { time!("myfunction: x={}", x); // A std::thread::sleep(Duration::from_secs(1)); { time!("inner operation"); // B std::thread::sleep(Duration::from_secs(2)); // timer B ends here } x + 1 // timer A ends here } }
Output
Jan 20 11:28:46.841 INFO relayer::macros::profiling: ⏳ myfunction: x=42 - start
Jan 20 11:28:47.842 INFO relayer::macros::profiling: ⏳ inner operation - start
Jan 20 11:28:49.846 INFO relayer::macros::profiling: ⏳ inner operation - elapsed: 2004ms
Jan 20 11:28:49.847 INFO relayer::macros::profiling: ⏳ myfunction: x=42 - elapsed: 3005ms
Profiling is useful for tracking down unusually slow methods.
Each transaction or query usually consists of multiple lower-level methods,
and it's often not clear which of these are the culprit for low performance.
With profiling enabled, hermes
will output timing information for individual
methods involved in a command.
NOTE: To be able to see the profiling output, the realyer needs to be compiled with
the profiling
feature and the log level should be info
level or lower.
Example output for tx conn-init
command
hermes --config config.toml tx conn-init --b-chain ibc-0 --a-chain ibc-1 --b-client 07-tendermint-0 --a-client 07-tendermint-0
Apr 13 20:58:21.225 INFO ibc_relayer::macros::profiling: ⏳ init_light_client - start
Apr 13 20:58:21.230 INFO ibc_relayer::macros::profiling: ⏳ init_light_client - elapsed: 4ms
Apr 13 20:58:21.230 INFO ibc_relayer::macros::profiling: ⏳ init_event_monitor - start
Apr 13 20:58:21.235 INFO ibc_relayer::macros::profiling: ⏳ init_event_monitor - elapsed: 5ms
Apr 13 20:58:21.235 INFO ibc_relayer::event::monitor: running listener chain.id=ibc-1
Apr 13 20:58:21.236 INFO ibc_relayer::macros::profiling: ⏳ init_light_client - start
Apr 13 20:58:21.239 INFO ibc_relayer::macros::profiling: ⏳ init_light_client - elapsed: 2ms
Apr 13 20:58:21.239 INFO ibc_relayer::macros::profiling: ⏳ init_event_monitor - start
Apr 13 20:58:21.244 INFO ibc_relayer::macros::profiling: ⏳ init_event_monitor - elapsed: 4ms
Apr 13 20:58:21.244 INFO ibc_relayer::event::monitor: running listener chain.id=ibc-0
Apr 13 20:58:21.244 INFO ibc_relayer::macros::profiling: ⏳ get_signer - start
Apr 13 20:58:21.246 INFO ibc_relayer::macros::profiling: ⏳ get_signer - elapsed: 1ms
Apr 13 20:58:21.246 INFO ibc_relayer::macros::profiling: ⏳ query_latest_height - start
Apr 13 20:58:21.246 INFO ibc_relayer::macros::profiling: ⏳ block_on - start
Apr 13 20:58:21.248 INFO ibc_relayer::macros::profiling: ⏳ block_on - elapsed: 1ms
Apr 13 20:58:21.249 INFO ibc_relayer::macros::profiling: ⏳ query_latest_height - elapsed: 3ms
Apr 13 20:58:21.250 INFO ibc_relayer::macros::profiling: ⏳ unbonding_period - start
Apr 13 20:58:21.250 INFO ibc_relayer::macros::profiling: ⏳ block_on - start
Apr 13 20:58:21.251 INFO ibc_relayer::macros::profiling: ⏳ block_on - elapsed: 0ms
Apr 13 20:58:21.270 INFO ibc_relayer::macros::profiling: ⏳ block_on - start
Apr 13 20:58:21.273 INFO ibc_relayer::macros::profiling: ⏳ block_on - elapsed: 2ms
Apr 13 20:58:21.273 INFO ibc_relayer::macros::profiling: ⏳ unbonding_period - elapsed: 23ms
Apr 13 20:58:21.279 INFO ibc_relayer::macros::profiling: ⏳ build_consensus_state - start
Apr 13 20:58:21.280 INFO ibc_relayer::macros::profiling: ⏳ build_consensus_state - elapsed: 0ms
Apr 13 20:58:21.280 INFO ibc_relayer::macros::profiling: ⏳ send_msgs - start
Apr 13 20:58:21.280 INFO ibc_relayer::macros::profiling: ⏳ send_tx - start
Apr 13 20:58:21.282 INFO ibc_relayer::macros::profiling: ⏳ PK "03f17d2c094ee68cfcedb2c2f2b7dec6cd82ea158ac1c32d3de0ca8b288a3c8bfa" - start
Apr 13 20:58:21.282 INFO ibc_relayer::macros::profiling: ⏳ block_on - start
Apr 13 20:58:21.285 INFO ibc_relayer::macros::profiling: ⏳ block_on - elapsed: 3ms
Apr 13 20:58:21.296 INFO ibc_relayer::macros::profiling: ⏳ block_on - start
Apr 13 20:58:22.664 INFO ibc_relayer::macros::profiling: ⏳ block_on - elapsed: 1367ms
Apr 13 20:58:22.664 INFO ibc_relayer::macros::profiling: ⏳ PK "03f17d2c094ee68cfcedb2c2f2b7dec6cd82ea158ac1c32d3de0ca8b288a3c8bfa" - elapsed: 1382ms
Apr 13 20:58:22.664 INFO ibc_relayer::macros::profiling: ⏳ send_tx - elapsed: 1384ms
Apr 13 20:58:22.664 INFO ibc_relayer::macros::profiling: ⏳ send_msgs - elapsed: 1384ms
Success: CreateClient(
CreateClient(
Attributes {
height: Height {
revision: 0,
height: 10675,
},
client_id: ClientId(
"07-tendermint-7",
),
client_type: Tendermint,
consensus_height: Height {
revision: 1,
height: 10663,
},
},
),
)
Glossary
These are some of the definitions used in this guide:
Term | Definition |
---|---|
IBC transaction | A transaction that includes IBC datagrams (including packets). This is constructed by the relayer and sent over the physical network to a chain according to the chain rules. For example, for tendermint chains a broadcast_tx_commit request is sent to a tendermint RPC server. |
IBC datagram | An element of the transaction payload sent by the relayer; it includes client, connection, channel and IBC packet data. Multiple IBC datagrams may be included in an IBC transaction. |
IBC packet | A particular type of IBC datagram that includes the application packet and its commitment proof. |
IBC Client | Client code running on chain, typically only the light client verification related functionality. |
Relayer Light Client | Full light client functionality, including connecting to at least one provider (full node), storing and verifying headers, etc. |
Source chain | The chain from which the relayer reads data to fill an IBC datagram. |
Destination chain | The chain where the relayer submits transactions that include the IBC datagram. |