COSMOS WIKI
July 31, 2022

IBC через Hermes

Hermes это ретранслятор (relayer) межсетевых сетей Cosmos, построенный на Rust. Простыми словами это протокол связи между блокчейнами. Ретранслятор является центральным элементом в сетевой архитектуре IBC и обеспечивает извелечение и сбор информации от сетей (блокчейнов) которые не могут напрямую отправлять сообщения друг другу. Подробнее о Hermes можно почитать здесь

Hermes является не единственной реализацией cosmos ретрансляторов, к примеру еще есть, ibc-go и ts-relayer, но именно Hermes завоевал популярность и является выбором большинства нодраннеров

В данном гайде будет настроен relayer между сетями Stride и Gaia, но можно использовать любые поддерживаемые сети. Команда Stride предложила не создавать отдельные каналы, а поддержать эти:

Команда Stride рекомендует подключать каналы ниже

Active STRIDE <> GAIA Channels:

CHANNEL_ID | PORT_ID
-----------+-------------------------------
channel-0  |  transfer
channel-1  |  icacontroller-GAIA.DELEGATION
channel-2  |  icacontroller-GAIA.FEE
channel-3  |  icacontroller-GAIA.WITHDRAWAL
channel-4  |  icacontroller-GAIA.REDEMPTION

Подготовка

Любой ретранслятор можно установить как на 1 сервер вместе с необходимыми нодами, так и на отдельный сервер, при этом даже не обязательно поднимать собственные ноды, а достаточно знать открытые RPC рабочих нод. Но, поскольку успех ретрансляции сильно зависит от задержки и скорости ввода-вывода, то в настоящее время рекомендуется обслуживать узлы на том же компьютере, что и процесс ретрансляции

Поскольку процесс ретрансляции должен иметь возможность запрашивать сеть обратно по высоте в течение как минимум 2/3 unbonding period (trusting period = 2/3 от unbonding period), то рекомендуется использовать настройки прунинга, которые сохранят полное состояние цепочки в течение более длительного периода времени, чем период unbonding

То есть 14 дней от 21 или 10 дней от 14 или 1,3 дня от 2 дней

Необходимый прунинг можно посчитать по формуле 14 дней*24*60*60/ среднее время блока

  • Для 14 дней pruning-keep-recent = 200000
  • Для 10 дней pruning-keep-recent = 150000
  • Для 1,3 дня pruning-keep-recent = 18720
  • Для 1,5 часа pruning-keep-recent = 1000
Polkachu к примеру для relayer использует 40000/2000

В данном примере мы будем использовать 1 сервер, на котором установим как сами ноды, так и Hermes

Прежде всего нам необходимо:

- установить две необходимые ноды и полностью синхронизировать их. В данном примере устанавливаем Stride и Gaia

- minimum-gas-prices на нодах настраиваем на 0 (minimum-gas-prices = "0uatom" / minimum-gas-prices = "0ustrd")

- RPC должны быть открыты и доступны Hermes (как это сделать можно узнать здесь)

- индексация должна быть настроена в положение indexer = "kv" (если, не меняли, то пропускаем этот пункт)

- проверить наличие токенов на кошельках. Лучше всего использовать одну и ту же мнемонику во всех сетях, не используйте свои адреса ретрансляции ни для чего другого, потому что это может привести к ошибкам в последовательности несогласованных учетных записей

- после этого желательно проверить поддерживается ли данными нодами сама ретрансляция

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

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

Установка Hermes

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

# проверяем версию
hermes version
# hermes 1.0.0-rc.1+54ae389

Настройка Hermes

Для начала задаем переменные. RPC_ADDR берем из config.toml - GRPC_ADDR берем из app.toml. Значение 127.0.0.1 заменяем на IP адрес сервера в случае установки Hermes отдельно от нод. Данные ниже даны для примера!!!

Chain_1

CHAIN_ID_1="STRIDE-TESTNET-4"
RPC_ADDR_1="127.0.0.1:26657"
GRPC_ADDR_1="127.0.0.1:9090"
ACCOUNT_PREFIX_1="stride"
TRUSTING_PERIOD_1="8hours"
DENOM_1="ustrd"
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="lesnik_utsa#4480"

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 dry another fiber belt year smoke glimpse extra fancy acquire method help universe"

Создаем config файл

В данном файле будут использоваться переменные указанные выше. За более детальной информацией по config с описанием переходим сюда

sudo tee $HOME/.hermes/config.toml > /dev/null <<EOF
[global]
# укажите уровень детализации вывода журнала ретранслятора Default: 'info'
# допустимые варианты '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]
# Следует ли включать службу REST или нет. Значение по умолчанию: false
enabled = true
host = '0.0.0.0'
port = 3000

[telemetry]
# Следует ли включать службу телеметрии или нет. Значение по умолчанию: 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

Хочу отдельно обратить внимание на config и его часть [chains.packet_filter]

Если не прописывать совсем эти параметры, то ретранслятор будет использовать все доступные каналы

Другие примеры:

[chains.packet_filter]
policy = 'allow'
list = [
  ['ica*', '*'], # разрешить ретрансляцию по всем каналам, порт которых начинается с 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'],
]

Проверяем правильность конфигурации

hermes config validate
# Success: "configuration is valid"

hermes health-check
#  INFO ThreadId(01) using default configuration from '/root/.hermes/config.toml'
#  INFO ThreadId(01) [STRIDE-TESTNET-2] performing health check...
#  INFO ThreadId(01) chain is healthy chain=STRIDE-TESTNET-2
#  INFO ThreadId(01) [GAIA] performing health check...
#  INFO ThreadId(01) chain is healthy chain=GAIA
#  SUCCESS performed health check for all chains in the config

Добавляем кошельки в Hermes. За подробной информацией обращаемся сюда

# создаем файлы с мнемноникой
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

# восстанавливаем кошельки
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

# посмотреть кошельки
hermes keys list --chain ${CHAIN_ID_1}
hermes keys list --chain ${CHAIN_ID_2}

# посмотреть баланс
hermes keys balance --chain ${CHAIN_ID_1}
hermes keys balance --chain ${CHAIN_ID_2}

Создаем 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

Если все прошло успешно, то видим примерно такое

2022-07-31T14:38:30.273575Z  INFO ThreadId(01) Scanned chains:
2022-07-31T14:38:30.273579Z  INFO ThreadId(01) # Chain: STRIDE-TESTNET-2
  - Client: 07-tendermint-0
    * Connection: connection-0
      | State: OPEN
      | Counterparty state: OPEN
      + Channel: channel-0
        | Port: transfer
        | State: OPEN
        | Counterparty: channel-0
# Chain: GAIA
  - Client: 07-tendermint-0
    * Connection: connection-0
      | State: OPEN
      | Counterparty state: OPEN
      + Channel: channel-0
        | Port: transfer
        | State: OPEN
        | Counterparty: channel-0
2022-07-31T14:38:30.273595Z  INFO ThreadId(01) connection is Open, state on destination chain is Open chain=STRIDE-TESTNET-2 connection=connection-0 counterparty_chain=GAIA
2022-07-31T14:38:30.273600Z  INFO ThreadId(01) connection is already open, not spawning Connection worker chain=STRIDE-TESTNET-2 connection=connection-0
2022-07-31T14:38:30.273606Z  INFO ThreadId(01) no connection workers were spawn chain=STRIDE-TESTNET-2 connection=connection-0
2022-07-31T14:38:30.273611Z  INFO ThreadId(01) channel is OPEN, state on destination chain is OPEN chain=STRIDE-TESTNET-2 counterparty_chain=GAIA channel=channel-0
2022-07-31T14:38:30.276438Z  INFO ThreadId(01) spawned client worker: client::GAIA->STRIDE-TESTNET-2:07-tendermint-0
2022-07-31T14:38:30.278403Z  INFO ThreadId(01) spawned packet worker: packet::channel-0/transfer:STRIDE-TESTNET-2->GAIA
2022-07-31T14:38:30.278430Z  INFO ThreadId(01) done spawning channel workers chain=STRIDE-TESTNET-2 channel=channel-0
2022-07-31T14:38:30.278484Z  INFO ThreadId(01) spawning Wallet worker: wallet::STRIDE-TESTNET-2
2022-07-31T14:38:30.278501Z  INFO ThreadId(01) connection is Open, state on destination chain is Open chain=GAIA connection=connection-0 counterparty_chain=STRIDE-TESTNET-2
2022-07-31T14:38:30.278506Z  INFO ThreadId(01) connection is already open, not spawning Connection worker chain=GAIA connection=connection-0
2022-07-31T14:38:30.278510Z  INFO ThreadId(01) no connection workers were spawn chain=GAIA connection=connection-0
2022-07-31T14:38:30.278513Z  INFO ThreadId(01) channel is OPEN, state on destination chain is OPEN chain=GAIA counterparty_chain=STRIDE-TESTNET-2 channel=channel-0
2022-07-31T14:38:30.283017Z  INFO ThreadId(01) spawned client worker: client::STRIDE-TESTNET-2->GAIA:07-tendermint-0
2022-07-31T14:38:30.286036Z  INFO ThreadId(01) done spawning channel workers chain=GAIA channel=channel-0
2022-07-31T14:38:30.286132Z  INFO ThreadId(01) spawning Wallet worker: wallet::GAIA
2022-07-31T14:38:30.290684Z  INFO ThreadId(01) Hermes has started

Дальше должны пойти подобные логи с хешами транзакций и баланс кошельков должен начать уменьшаться. Транзакции также можно проверить в explorer. Если есть такие логи, значит все настроено и работает

Полезные команды

Открыть канал

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

Смотрим логи

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

Отправить транзакции

strided tx ibc-transfer transfer transfer channel-0 <cosmos1fzhv> 500ustrd --from <name_stride_wallet> --fees 500ustrd -y
gaiad tx ibc-transfer transfer transfer channel-0 <stride1fz> 500uatom --from <name_gaia_wallet> --fees 500uatom -y

# примеры
# strided tx ibc-transfer transfer transfer channel-0 cosmos1fzhv743ve4nw99lqwr9f8x4vkngp7v5ma008df 100000ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2 --from wallet -y
# strided tx ibc-transfer transfer transfer channel-0 cosmos1fzhv743ve4nw99lqwr9f8x4vkngp7v5ma008df 100000ustrd --from wallet --fees 500ustrd -y
# команда просмотра порта RPC
echo $(grep -A 3 "\[rpc\]" ~/.sei/config/config.toml | egrep -o ":[0-9]+")

Удалить ретранслятор

systemctl stop hermesd && \
systemctl disable hermesd && \
rm /etc/systemd/system/hermesd.service && \
systemctl daemon-reload && \
cd $HOME && \
rm -rf .hermes && \
rm -rf $(which hermesd)
Поддержим природу вместе: 0xa7476EC14cD663C742d527113638C77a1631Cc89 cosmos1m2mm7kzvw6m3464h94ryqg8xvr6tyeyyecppnq

Выражаю благодарность: за гайд от kj89, который очень помог в написании данного гайда


Полезная литература: