# Interchain Security: IBC Timeouts This document will summarize how IBC timeouts can affect consumer chains in an Interchain Security environment. - How to query that a client has expired? ``` interchain-security-pd q ibc client status 07-tendermint-0 --home ~/.isp status: Expired ``` ## Scenario 1: IBC Relayer is turned off longer than the timeout period Check: - [X] The timeout period is set by the trusting period in the Hermes config file. - [X] The trusting period must be shorter than the unbonding period In order to test this, we only need a single-validator environment with two chains connected to each other. We can test this in a non-Interchain Security setup first to simplify things. ### Test 1: Same trusting period and unbonding time on both chains - Start both chains (a and b) ```yaml --- # yamllint disable rule:line-length all: vars: chain_home_clear: true chain_version: v7.0.0 chain_create_validator: true ansible_user: root reboot: false pruning: nothing p2p_allow_duplicate_ip: true chain_gov_testing: true chain_voting_period: 60s api_enabled: true grpc_web_enabled: false children: node: hosts: ibc-test.stg.earthball.xyz: fast_sync: false chain_id: chain-a chain_moniker: chain-a node_user: chain-a chain_home: "{{node_user_home}}/.gaia" cosmovisor_service_name: cv-chain-a node_user: chain-a api_port: 26650 rpc_port: 26651 p2p_port: 26652 grpc_port: 26653 rpc_pprof_port: 26654 config_proxy_app: 'tcp://127.0.0.1:26655' ibc-test-2.stg.earthball.xyz: fast_sync: false chain_id: chain-b chain_moniker: chain-b node_user: chain-b chain_home: "{{node_user_home}}/.gaia" cosmovisor_service_name: cv-chain-b node_user: chain-b api_port: 26660 rpc_port: 26661 p2p_port: 26662 grpc_port: 26663 rpc_pprof_port: 26664 config_proxy_app: 'tcp://127.0.0.1:26665' ``` - Set the unbonding period to 2 minutes in both chains Proposal to set the unbonding period to 2minutes: ```json { "title":"Unbonding period - 2 minutes", "description":"Unbonding period update", "changes":[ { "subspace":"staking", "key":"UnbondingTime", "value":"120000000000" } ], "deposit":"1uatom" } ``` To submit and vote on the proposal: ``` gaiad tx gov submit-proposal param-change unbonding-2m.json --home ~/.gaia --from validator -y gaiad tx gov vote 1 yes --from validator -y ``` - Set the trusting period to Hermes config to 1 minute (if it lets us) for both chains Inventory file: ``` --- # yamllint disable rule:line-length all: vars: chain_home_clear: true chain_version: v7.0.2 hermes_version: v1.0.0 ansible_user: root children: node: hosts: my-chain-1.dev.testnet.com: chain_id: my-chain-1 chain_create_validator: true chain_airdrop: true chain_airdrop_accounts: - cosmos1r5v5srda7xfth3hn2s26txvrcrntldjumt8mhl # relayer account my-chain-2.dev.testnet.com: chain_id: my-chain-2 chain_create_validator: true chain_airdrop: true chain_airdrop_accounts: - cosmos1r5v5srda7xfth3hn2s26txvrcrntldjumt8mhl # relayer account hermes: hosts: ibc-test.stg.earthball.xyz: hermes_relayer_mnemonics: true hermes_chains: chain-a: hermes_relayer_mnemonic: 'chaina-mnemonic.txt' hermes_port_name: transfer hermes_chain_rpc_url_schema: http hermes_chain_rpc_hostname: ibc-test.stg.earthball.xyz hermes_chain_rpc_port: 26651 hermes_chain_grpc_url_schema: http hermes_chain_grpc_hostname: ibc-test.stg.earthball.xyz hermes_chain_grpc_port: 26653 hermes_chain_websocket_url_schema: ws hermes_chain_rpc_timeout: '10s' hermes_chain_account_prefix: 'cosmos' hermes_chain_key_name: 'testkey' hermes_chain_store_prefix: 'ibc' hermes_chain_max_gas: 2000000 hermes_chain_fee_granter: '' gas_price: "{ price = 0.001, denom = 'uatom' }" hermes_chain_gas_multiplier: 1.1 hermes_chain_clock_drift: '5s' hermes_chain_trusting_period: '60seconds' hermes_chain_trust_threshold: "{ numerator = '1', denominator = '3' }" chain-b: hermes_relayer_mnemonic: 'chainb-mnemonic.txt' hermes_port_name: transfer hermes_chain_rpc_url_schema: http hermes_chain_rpc_hostname: ibc-test.stg.earthball.xyz hermes_chain_rpc_port: 26661 hermes_chain_grpc_url_schema: http hermes_chain_grpc_hostname: ibc-test.stg.earthball.xyz hermes_chain_grpc_port: 26663 hermes_chain_websocket_url_schema: ws hermes_chain_rpc_timeout: '10s' hermes_chain_account_prefix: 'cosmos' hermes_chain_key_name: 'testkey' hermes_chain_store_prefix: 'ibc' hermes_chain_max_gas: 2000000 hermes_chain_fee_granter: '' gas_price: "{ price = 0.001, denom = 'uatom' }" hermes_chain_gas_multiplier: 1.1 hermes_chain_clock_drift: '5s' hermes_chain_trusting_period: '60seconds' hermes_chain_trust_threshold: "{ numerator = '1', denominator = '3' }" ``` - Create a connection, client, and channel ``` hermes create connection --a-chain chain-a --b-chain chain-b hermes create channel --a-chain chain-a --a-port transfer --b-port transfer --order unordered --a-connection connection-0 022-10-25T18:47:29.102257Z ERROR ThreadId(01) [chain-a -> chain-b:07-tendermint-0] client state is not valid: latest height is outside of trusting period! latest_height=0-152 network_timestmap=Timestamp(2022-10-25T18:47:24.020163694Z) consensus_state_timestamp=Timestamp(2022-10-25T18:45:38.622427593Z) elapsed=105.397736101s 2022-10-25T18:47:29.102328Z ERROR ThreadId(01) failed ChanOpenTry ChannelSide { chain: BaseChainHandle { chain_id: ChainId { id: "chain-b", version: 0 }, runtime_sender: Sender { .. } }, client_id: ClientId("07-tendermint-0"), connection_id: ConnectionId("connection-0"), port_id: PortId("transfer"), channel_id: None, version: None }: failed during an operation on client '07-tendermint-0' hosted by chain 'chain-b': client 07-tendermint-0 on chain id chain-b is expired or frozen: expired: time elapsed since last client update: 105.397736101s 2022-10-25T18:47:29.102353Z ERROR ThreadId(01) failed to open channel after 2 retries ERROR Error after maximum retry of 2 and total delay of 3s: failed to finish channel handshake for Channel { ordering: Unordered, a_side: ChannelSide { chain: BaseChainHandle { chain_id: ChainId { id: "chain-a", version: 0 }, runtime_sender: Sender { .. } }, client_id: ClientId("07-tendermint-0"), connection_id: ConnectionId("connection-0"), port_id: PortId("transfer"), channel_id: Some(ChannelId("channel-0")), version: None }, b_side: ChannelSide { chain: BaseChainHandle { chain_id: ChainId { id: "chain-b", version: 0 }, runtime_sender: Sender { .. } }, client_id: ClientId("07-tendermint-0"), connection_id: ConnectionId("connection-0"), port_id: PortId("transfer"), channel_id: None, version: None }, connection_delay: 0ns } ``` It looks like the amount of time it took to create the channel pushed us over the 1m trusting period. **Try setting unbonding time to 10 minutes and trusting period to 5m instead.** Polling the client on `chain-a` results in an "expired" status. We create a new client on both chains: ``` hermes create client --host-chain chain-a --reference-chain chain-b 2022-10-25T18:58:54.866241Z INFO ThreadId(01) using default configuration from '/home/hermes/.hermes/config.toml' 2022-10-25T18:58:54.942770Z INFO ThreadId(13) wait_for_block_commits: waiting for commit of tx hashes(s) 25BEE62CFD3FE93911D0AE5C67D0F969C7AEB06EEA6E08DBEA47F1033B3324E8 id=chain-a SUCCESS CreateClient( CreateClient( Attributes { client_id: ClientId( "07-tendermint-1", ), client_type: Tendermint, consensus_height: Height { revision: 0, height: 338, }, }, ), ) hermes create client --host-chain chain-b --reference-chain chain-a 2022-10-25T18:59:12.539103Z INFO ThreadId(01) using default configuration from '/home/hermes/.hermes/config.toml' 2022-10-25T18:59:12.574193Z INFO ThreadId(13) wait_for_block_commits: waiting for commit of tx hashes(s) F0208F9417E6E5FC5081FA28DDB7EE8CF626E812BBB3270DB43D9B602B5BEA82 id=chain-b SUCCESS CreateClient( CreateClient( Attributes { client_id: ClientId( "07-tendermint-1", ), client_type: Tendermint, consensus_height: Height { revision: 0, height: 310, }, }, ), ) ``` Then we create a connection between those new `07-tendermint-1` clients: ``` hermes create connection --a-chain chain-a --a-client 07-tendermint-1 --b-client 07-tendermint-1 ... SUCCESS Connection { delay_period: 0ns, a_side: ConnectionSide { chain: BaseChainHandle { chain_id: ChainId { id: "chain-a", version: 0, }, runtime_sender: Sender { .. }, }, client_id: ClientId( "07-tendermint-1", ), connection_id: Some( ConnectionId( "connection-1", ), ), }, b_side: ConnectionSide { chain: BaseChainHandle { chain_id: ChainId { id: "chain-b", version: 0, }, runtime_sender: Sender { .. }, }, client_id: ClientId( "07-tendermint-1", ), connection_id: Some( ConnectionId( "connection-1", ), ), }, } ``` Lastly, we create a channel on the new `connection-0`. ``` hermes create channel --a-chain chain-a --a-connection connection-0 --a-port transfer --b-port transfer SUCCESS Channel { ordering: Unordered, a_side: ChannelSide { chain: BaseChainHandle { chain_id: ChainId { id: "chain-a", version: 0, }, runtime_sender: Sender { .. }, }, client_id: ClientId( "07-tendermint-1", ), connection_id: ConnectionId( "connection-1", ), port_id: PortId( "transfer", ), channel_id: Some( ChannelId( "channel-3", ), ), version: None, }, b_side: ChannelSide { chain: BaseChainHandle { chain_id: ChainId { id: "chain-b", version: 0, }, runtime_sender: Sender { .. }, }, client_id: ClientId( "07-tendermint-1", ), connection_id: ConnectionId( "connection-1", ), port_id: PortId( "transfer", ), channel_id: Some( ChannelId( "channel-0", ), ), version: None, }, connection_delay: 0ns, } ``` - Start hermes - Poll the client on chain a, make sure it's online/active ``` gaiad q ibc client status 07-tendermint-1 status: Active ``` - Stop hermes and start the timer - Poll the client on chain a before the trusting period is over It was active up until minute 4, and it expired before reaching minute 5. It's likely hermes had not sent a refresh message recently when we stopped the hermes service. - Poll the client on chain a after the trusting period has elapsed - Poll the client on chain a after the unbonding period has elapsed - Start hermes again - Poll the client on chain a The client should be expired. How do we bring back the client? See this doc: https://hermes.informal.systems/commands/upgrade/test.html #### Bringing back an expired client - Submit an upgrade chain command from hermes ``` hermes tx upgrade-chain --reference-chain chain-a --host-chain chain-b --host-client 07-tendermint-0 --amount 1 --denom uatom --height-offset 60 --upgrade-name v7-Theta ``` - Query the resulting gov proposal on chain a ``` gaiad --node tcp://localhost:26651 query gov proposal 1 ontent: '@type': /ibc.core.client.v1.UpgradeProposal description: upgrade the chain software and unbonding period plan: height: "13751" 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: chain-a frozen_height: revision_height: "0" revision_number: "0" latest_height: revision_height: "13752" 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: 600s upgrade_path: - upgrade - upgradedIBCState deposit_end_time: "2022-10-28T14:28:39.818469616Z" final_tally_result: abstain: "0" "no": "0" no_with_veto: "0" "yes": "8000000000" proposal_id: "4" status: PROPOSAL_STATUS_PASSED submit_time: "2022-10-26T14:28:39.818469616Z" total_deposit: - amount: "1" denom: uatom voting_end_time: "2022-10-26T14:28:49.818469616Z" voting_start_time: "2022-10-26T14:28:39.818469616Z" ``` - Vote on the resulting gov proposal on chain a ``` gaiad --node tcp://localhost:26651 tx gov vote 2 yes --from validator -y ``` Collect the upgrade height from the gov proposal and list it in the next command: 409 - Submit the upgrade client command from hermes ``` hermes upgrade client --host-chain chain-b --client 07-tendermint-0 --upgrade-height 1578 ``` The above workflow hasn't worked because we are not upgrading the binary. We'll try submitting a proposal manually rather than using hermes to do it. #### Manual IBC client upgrade 1. Create a new client to replace the expired one. On the hermes machine: ``` hermes create client --host-chain chain-a --reference-chain chain-b hermes create client --host-chain chain-b --reference-chain chain-a ``` 2. Submit the gov proposal ```json { "messages": [ { "@type": "/ibc.core.client.v1.ClientUpdateProposal", "title": "Client revival", "description": "Update 07-tendermint-0 client", "subject_client_id": "07-tendermint-0", "substitute_client_id": "07-tendermint-1" } ], "metadata": "<metadata>", "deposit": "1uatom" } ``` Proposal submission (the above file is not used with gaia v7): ``` gaiad tx gov submit-proposal update-client 07-tendermint-0 07-tendermint-1 --from validator --title "Replace expired client" --description "New client" ``` Vote yes on proposal and make sure it passes Restart hermes Submit and pass proposal on the other chain Restart hermes Test ibc transfers ### Test 2: Different timeout periods on the chains - Set the unbonding period to 10 minutes in chain a and 20 minutes in chain b - Start both chains (a and b) - Set the trusting period to Hermes config to 5 minutes in chain a and 10 minutes in chain b - Create a connection and channel - Start hermes - Poll the client on chain a, make sure it's online/active - Stop hermes and start the timer - Poll the client on chain a before the trusting period is over in chain a - Poll the client on chain a after the trusting period has elapsed in chain a - Poll the client on chain a after the unbonding period has elapsed in chain a - Start hermes again - Poll the client on chain a - Poll the client on chain b The client should be expired. Notes: