January 31, 2023

IBC via Hermes

Hermes is a relayer of Cosmos interconnect networks built on Rust. In simple words, it is a communication protocol between blockchains. The relayer is a central element in the IBC network architecture and provides the isolation and collection of information from networks (blockchains) that cannot directly send messages to each other.

Hermes is not the only one implementation of cosmos relays, there are also ibc-go and ts-relayer for example, but Hermes is very popular and is a choice of most nodrunners.

This guide will set up a relayer between the Jackal and Gaia networks, but you can use any supported networks.

Preparing

Any repeater can be installed either on 1 server together with the necessary nodes, or on a separate server, and it is not even necessary to raise your own nodes, and it is enough to know the open RPC of the working nodes. However, since the success of relaying strongly depends on latency and I/O rate, it is currently recommended to serve nodes on the same computer as the relaying process

Since the retransmission process must be able to query the network back by height for at least 2/3 of the unbonding period (trusting period = 2/3 of the unbonding period), it is recommended to use pruning settings which will keep the full chain state for a longer period of time than the unbonding period

That is, 14 days of 21 or 10 days of 14.

The pruning needed can be calculated by the formula 14 days*24*60*60/ average block time

For 14 days pruning-keep-recent = 200000
For 10 days pruning-keep-recent = 150,000

In this example we will use 1 server, where we install both the nodes themselves and Hermes

First of all, we need to:

- install the two necessary nodes and fully synchronize them. In this example, we install Jackal and Gaia

- Set the minimum-gas-prices on the nodes to 0 (minimum-gas-prices = "0uatom" / minimum-gas-prices = "0utori")

- RPC's must be open and available to Hermes

- Indexing should be set to indexer = "kv" (if you haven't changed it, skip this point)

- Check the availability of tokens on wallets. It is best to use the same mnemonic in all networks, do not use your relay addresses for anything else, because this can lead to errors in the sequence of uncoordinated accounts

- after that it is desirable to check whether these nodes support relaying itself

strided q ibc-transfer params
# receive_enabled: true
# send_enabled: true

gaiad q ibc-transfer params
# receive_enabled: true
# send_enabled: true

Hermes installation

mkdir -p $HOME/.hermes/bin

wget https://github.com/informalsystems/ibc-rs/releases/download/v1.0.0-rc.1/hermes-v1.0.0-rc.1-x86_64-unknown-linux-gnu.tar.gz && \
tar -C $HOME/.hermes/bin/ -vxzf hermes-v1.0.0-rc.1-x86_64-unknown-linux-gnu.tar.gz && \
echo 'export PATH="$HOME/.hermes/bin:$PATH"' >> $HOME/.bash_profile && \
source $HOME/.bash_profile

# check the version
hermes version
# hermes 1.0.0-rc.1+54ae389

Setting up Hermes

First, we set the variables. RPC_ADDR from config.toml - GRPC_ADDR from app.toml. Replace the value 127.0.0.1 with the server IP address in case you installed Hermes separately from the nodes. The data below are for example!!!

Chain_1

CHAIN_ID_1="jackal-1"
RPC_ADDR_1="127.0.0.1:26657"
GRPC_ADDR_1="127.0.0.1:9090"
ACCOUNT_PREFIX_1="jackal"
TRUSTING_PERIOD_1="8hours"
DENOM_1="ujkl"
MNEMONIC_1="way festival bargain mass swear flat fish help dinosaur delay lemon dry another fiber belt year smoke glimpse extra fancy acquire method help universe"
MEMO="Relayed by moodman"

Chain_2

CHAIN_ID_2="GAIA"
RPC_ADDR_2="127.0.0.1:36657"
GRPC_ADDR_2="127.0.0.1:9190"
ACCOUNT_PREFIX_2="cosmos"
TRUSTING_PERIOD_2="8hours"
DENOM_2="uatom"
MNEMONIC_2="way festival bargain mass swear flat fish help dinosaur delay lemon dr

Create a config file

This file will use the variables listed above.

sudo tee $HOME/.hermes/config.toml > /dev/null <<EOF
[global]
# specify the level of detail of the repeater log output Default: 'info'
# allowed variants 'error','warn','info','debug','trace'
log_level = 'info'

[mode]

[mode.clients]
enabled = true
refresh = true
misbehaviour = true

[mode.connections]
enabled = false

[mode.channels]
enabled = false

[mode.packets]
enabled = true
clear_interval = 100
clear_on_start = true
tx_confirmation = true

[rest]
# Whether to enable the REST service or not. Default value: false
enabled = true
host = '0.0.0.0'
port = 3000

[telemetry]
# Whether the telemetry service should be enabled or not. Default value: false
enabled = true
host = '0.0.0.0'
port = 3001

[[chains]]
### CHAIN_A ###
id = '${CHAIN_ID_1}'
rpc_addr = 'http://${RPC_ADDR_1}'
grpc_addr = 'http://${GRPC_ADDR_1}'
websocket_addr = 'ws://${RPC_ADDR_1}/websocket'
rpc_timeout = '10s'
account_prefix = '${ACCOUNT_PREFIX_1}'
key_name = 'wallet'
address_type = { derivation = 'cosmos' }
store_prefix = 'ibc'
default_gas = 100000
max_gas = 2500000
gas_price = { price = 0.001, denom = '${DENOM_1}' }
gas_multiplier = 1.1
max_msg_num = 30
max_tx_size = 2097152
clock_drift = '5s'
max_block_time = '30s'
trusting_period = '${TRUSTING_PERIOD_1}'
trust_threshold = { numerator = '1', denominator = '3' }
memo_prefix = '${MEMO} Relayer'
[chains.packet_filter]
policy = 'allow'
list = [
  ['transfer', 'channel-0'],
]

[[chains]]
### CHAIN_2 ###
id = '${CHAIN_ID_2}'
rpc_addr = 'http://${RPC_ADDR_2}'
grpc_addr = 'http://${GRPC_ADDR_2}'
websocket_addr = 'ws://${RPC_ADDR_2}/websocket'
rpc_timeout = '10s'
account_prefix = '${ACCOUNT_PREFIX_2}'
key_name = 'wallet'
address_type = { derivation = 'cosmos' }
store_prefix = 'ibc'
default_gas = 100000
max_gas = 2500000
gas_price = { price = 0.001, denom = '${DENOM_2}' }
gas_multiplier = 1.1
max_msg_num = 30
max_tx_size = 2097152
clock_drift = '5s'
max_block_time = '30s'
trusting_period = '${TRUSTING_PERIOD_2}'
trust_threshold = { numerator = '1', denominator = '3' }
memo_prefix = '${MEMO} Relayer'
[chains.packet_filter]
policy = 'allow'
list = [
  ['transfer', 'channel-0'],
]
EOF

I would like to pay special attention to config and its part [chains.packet_filter]

If you do not write these parameters at all, then repeater will use all available channels

Other examples:

[chains.packet_filter]
policy = 'allow'
list = [
  ['ica*', '*'], # Allow rebroadcasting on all channels whose port starts with ica
  ['transfer', 'channel-0'],
]
[chains.packet_filter]
policy = 'allow'
list = [
 ['transfer', 'channel-0'],# gaia
 ['icahost', 'channel-4'],
 ['icahost', 'channel-1'],
 ['icahost', 'channel-2'],
 ['icahost', 'channel-3'],
]
[chains.packet_filter]
policy = 'allow'
list = [
 ['transfer', 'channel-0'],
]

Checking the correct configuration

hermes config validate

# Success: "configuration is valid"

hermes health-check

# INFO ThreadId(01) using default configuration from '/root/.hermes/config.toml'

Adding purses to Hermes.

# create files with mnemonics
sudo tee $HOME/.hermes/${CHAIN_ID_1}.mnemonic > /dev/null <<EOF
${MNEMONIC_1}
EOF
sudo tee $HOME/.hermes/${CHAIN_ID_2}.mnemonic > /dev/null <<EOF
${MNEMONIC_2}
EOF

# restore wallets
hermes keys add --chain ${CHAIN_ID_1} --mnemonic-file $HOME/.hermes/${CHAIN_ID_1}.mnemonic
hermes keys add --chain ${CHAIN_ID_2} --mnemonic-file $HOME/.hermes/${CHAIN_ID_2}.mnemonic
# Success: Restore key testkey ([ADDRESS]) on [CHAIN ID] chain
# Success: Restore key testkey ([ADDRESS]) on [CHAIN ID] chain

# see wallets
hermes keys list --chain ${CHAIN_ID_1}
hermes keys list --chain ${CHAIN_ID_2}

# see balance
hermes keys balance --chain ${CHAIN_ID_1}
hermes keys balance --chain ${CHAIN_ID_2}

Creating service

sudo tee /etc/systemd/system/hermesd.service > /dev/null <<EOF
[Unit]
Description=hermes
After=network-online.target

[Service]
User=$USER
ExecStart=$(which hermes) start
Restart=on-failure
RestartSec=3
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable hermesd
sudo systemctl restart hermesd && journalctl -u hermesd -f -o cat

Next should go similar logs with hashes of transactions and wallet balance should begin to decrease.

Useful Commands

Open a channel

hermes create channel --a-chain ${CHAIN_ID_1} --b-chain ${CHAIN_ID_2} --a-port transfer --b-port transfer --order unordered --new-client-connection

Looking at the logs

journalctl -u hermesd -f -o cat
journalctl -fn 100 -u hermesd | grep "Ok"
journalctl -u hermesd | grep "Ok"

Send transactions

canined tx ibc-transfer transfer transfer channel-0 <cosmos1fzhv> 500ujkl --from <name_jackal_wallet> --fees 500ujkl -y
gaiad tx ibc-transfer transfer transfer channel-0 <tori1fz> 500uatom --from <name_gaia_wallet> --fees 500uatom -y
# command to view the RPC port
echo $(grep -A 3 "\[rpc\]" ~/.sei/config/config.toml | egrep -o ":[0-9]+")

Remove the repeater

systemctl stop hermesd && \
systemctl disable hermesd && \
rm /etc/systemd/system/hermesd.service && \
systemctl daemon-reload && \
cd $HOME && \
rm -rf .hermes && \
rm -rf $(which hermesd)